Custom Plugins
Custom plugins are HTTP endpoints that extend claim logic. BitBadges sends a POST request to your endpoint during simulation, execution and/or post-success, and your response determines whether the plugin passes or fails.
Any logic you can express as an HTTP handler can become a claim plugin β authentication checks, API calls, database lookups, AI evaluations, webhook triggers, or anything else.
Getting Started
Go to bitbadges.io/developer β Plugins tab
Create a new plugin
Configure endpoint URL, schemas, and settings
Implement your handler
Test with the built-in claim tester
Finalize the version
Use the plugin in claims by its plugin ID
Architecture
User claims β BitBadges sends POST to your endpoint β You return 200 OK or errorThree parties:
Claim Creator β configures the claim, selects plugins, sets public/private params
Claiming User β attempts the claim, provides user inputs
Plugin Creator β builds and maintains the plugin endpoint (you)
The claim creator configures public/private params when adding the plugin to a claim. The user provides user inputs at claim time. All three are merged into a single request payload sent to your handler.
Handler Implementation
Context Info Reference
Every request includes these context fields:
Additionally, pluginSecret and version are included in every request. All custom params (public, private, user inputs) are merged into the top level of the request body alongside these fields.
Parameters
Three types of parameters, all merged into the handler payload:
Public params β Configured by the claim creator when adding the plugin to a claim. Visible in the claim's public configuration. Claim users can see these. Example: minimum token balance, a URL to display.
Private params β Configured by the claim creator. Hidden from claim users, only visible to BitBadges and the claim creator. Example: API keys, internal thresholds, webhook URLs.
User inputs β Provided by the user at claim time via a form rendered by BitBadges (or a custom frontend). Example: a code, an answer to a question, a file upload URL.
All three are defined as JsonBodyInputSchema[] arrays in your plugin's version config.
User Inputs
In-Site Forms (Recommended)
Define input schemas when creating your plugin. BitBadges renders a form for the user based on your schema.
Each field is defined as:
Supported types:
stringβ free text inputnumberβ numeric inputbooleanβ checkbox/toggledateβ date picker (value is UNIX ms)urlβ URL input with validation
Custom Frontend
If the in-site form isn't sufficient, redirect users to your own UI via a redirect URL. Typical flow:
User is redirected to your frontend
User authenticates / completes action on your side
You issue a one-time code or token
User enters the code back in-site
Your handler validates the code
Response Presets
Configure your plugin's stateFunctionPreset when creating a version. This determines how BitBadges handles your response and manages state.
Stateless
StatelessReturn 200 OK with an empty body. Plugin passes if status is 200. No state is tracked.
Best for: validation checks, API verifications, webhook triggers β anything where you just need pass/fail.
ClaimToken
ClaimTokenReturn a one-time use token that you generate. BitBadges tracks which tokens have been used. If the overall claim succeeds, the token is marked as used. If the claim fails (another plugin fails), the token remains available for retry.
Best for: custom authentication flows, external code validation, third-party integrations that issue tokens.
ClaimNumbers
ClaimNumbersReturn the claim number to assign for this attempt. Only one plugin per claim can use this preset.
Best for: custom claim number assignment logic (non-sequential, based on external data, etc.).
CustomResponseHandler
CustomResponseHandlerFull control over state management and response handling. You manage everything yourself.
Best for: advanced use cases that don't fit the other presets.
Error Handling
Timeout: All responses must arrive within 10 seconds. If your endpoint doesn't respond in time, the plugin fails.
Error format: Return
{ "message": "..." }with a non-200 status code. The message may be shown to the user β be informative but don't reveal secrets or internal details.Key restriction: Do not use
.in returned JSON keys (e.g., usebob@abc[dot]comnot[email protected]). This is a storage constraint.
Simulations (Dry Runs)
BitBadges simulates claims before real submission. Your plugin receives a simulation request first, then (if simulation passes) the real execution request.
Detection: _isSimulation === true and claimAttemptId is empty.
Your plugin should:
Validate inputs and check preconditions
Return the appropriate status code (200 = would pass, non-200 = would fail)
NOT execute side effects, mutate state, send notifications, or consume tokens
You can opt out of simulation requests by setting ignoreSimulations: true in your version config. If opted out, your plugin won't be called during simulation β it will only be called during real execution.
State Management
Critical rule: A 200 from your plugin does NOT mean the overall claim succeeded. Other plugins in the pipeline may fail, causing the claim to fail even though your plugin passed.
BitBadges-Managed State (Recommended)
Use response presets (Stateless, ClaimToken, ClaimNumbers). BitBadges only commits state changes if:
Your plugin returned 200, AND
The overall claim succeeded (all required plugins passed per the success logic)
This eliminates race conditions and ensures consistency. If the claim fails, any state your plugin would have changed is rolled back.
Self-Managed State
If you manage state in your own backend (external database, third-party API, etc.):
Don't update state on plugin success. Your plugin passing doesn't mean the claim succeeded.
Verify claim outcomes via the API before committing external state changes:
Handle race conditions. Multiple claim attempts can execute concurrently. Use idempotency keys or atomic operations in your backend.
Consider using
receiveStatusWebhookβ BitBadges can POST to your endpoint with the final claim outcome, so you don't have to poll.
Success Webhooks & Retry Strategy
If your plugin version has receiveStatusWebhook: true, BitBadges POSTs to your endpoint after the claim completes with _attemptStatus: 'success' or 'failure' in the body. This is separate from the plugin validation call.
Webhooks use exponential backoff for retries:
Base delay: 1 hour
Formula:
2^retries Γ base_delayMax delay: 7 days
Example: 1h β 2h β 4h β 8h β 16h β ...
Design your webhook handler to be idempotent β the same webhook may be delivered multiple times due to retries. Use the claimAttemptId as a deduplication key.
Plugin Version Config
Each plugin version contains the full configuration. When creating or updating a version in the developer portal, you configure all of these fields:
Field Details
finalized
Once finalized, a version is immutable and available to all users. Unfinalized versions are only usable by the plugin creator (for testing).
duplicatesAllowed
If true, a claim can include multiple instances of this plugin (each with different params). If false, only one instance is allowed.
requiresSessions
If true, the claim requires an active BitBadges session (user must be signed in).
requiresUserInputs
If true, the user must provide inputs at claim time (BitBadges renders a form). If false, the plugin operates with only creator-configured params.
reuseForNonIndexed
If true, the plugin is compatible with on-demand claims (stateless evaluation, no claim action needed). Requires the plugin to be stateless with no user inputs.
receiveStatusWebhook
If true, BitBadges POSTs to your endpoint after the claim completes with the final outcome. Useful for self-managed state β you learn whether the claim succeeded or failed without polling.
skipProcessingWebhook
If true, BitBadges auto-passes this plugin without calling your endpoint. Useful for display-only plugins (like custom-instructions) that don't need server-side validation.
ignoreSimulations
If true, your endpoint is not called during simulation/dry-run requests. The plugin auto-passes simulation.
requireSignIn
If true, the user must be signed in to BitBadges before this plugin is evaluated.
hardcodedInputs
Static key-value pairs always included in requests to your endpoint. Useful for API keys or configuration that doesn't change per claim.
passAddress
If true, the user's bitbadgesAddress and ethAddress are included in the payload. If false, the plugin operates without knowing the user's address.
customDetailsDisplay
A template string displayed to users in the claim UI. Use {{key}} to reference public param values. Example: "Must own at least {{minBalance}} tokens from collection {{collectionId}}".
Versioning
Plugins support multiple versions for safe iteration:
Creating versions: Each new version gets an incrementing version number. Start unfinalized for testing.
Finalizing: Once finalized, a version is immutable β schemas, endpoint URL, and all settings are locked. This protects claims that depend on your plugin from breaking changes.
Version selection: When a claim creator adds your plugin, they use the latest finalized version. That version is locked to the claim β even if you release new versions, existing claims stay on the version they were created with.
Testing unfinalized versions: Only the plugin creator can use unfinalized versions. Use the Claim Tester in the developer portal to test before finalizing.
Handler versioning: Your handler receives
versionandcreatedAtin every request. Use these to handle version-specific logic if needed (e.g., backwards-compatible changes to your handler that serves multiple versions).
Version Lifecycle
Publishing to the Plugin Directory
Plugins can be published to the BitBadges plugin directory, making them discoverable by other users.
Private plugins (default) β only you can add them to claims. Useful for internal tools, custom integrations, or plugins that require your specific backend.
Published plugins β visible in the plugin directory. Anyone can add them to their claims. Useful for building reusable tools that serve the broader BitBadges ecosystem.
To publish, configure your plugin's visibility settings in the developer portal. Published plugins should have clear descriptions, well-documented schemas, and stable, finalized versions.
Users can discover published plugins via the API:
Design Considerations
Plugin Success β Claim Success
Your plugin may pass but the claim fails (other plugins fail). Or your plugin fails but the claim succeeds (OR logic). Only update external state based on verified claim outcomes.
Parallel Execution
All plugins in a claim execute in parallel. Your plugin cannot depend on another plugin's state mutations within the same claim attempt. Each plugin sees state as it was before the attempt started.
Async Processing
Claims are processed asynchronously via a queue. Don't depend on real-time BitBadges claim state (like total claims completed) β it may be stale by the time your plugin runs. Your own parameters and the context info passed to your handler are safe to depend on.
Nested Claim Depth
If your plugin uses the satisfies-claim pattern (checking another claim's success), be aware that nested on-demand claims have a depth limit of 5 levels. Circular references are detected and rejected.
On-Demand Compatibility
For on-demand claims (stateless, no explicit claim action), your plugin must be:
Stateless β no per-attempt state tracking
No user inputs β operates with just address + hardcoded params
Context-only β works with only the passed context info
Set reuseForNonIndexed: true in your version config.
Authentication
BitBadges does not handle your authentication. If you need the user to authenticate with your service, manage auth end-to-end yourself. Common pattern:
User visits your frontend and authenticates
You issue a one-time code
User enters the code as a user input in-site
Your handler validates the code
Security
Verify
pluginSecreton every request to confirm BitBadges is the callerNever expose
pluginSecretclient-side β it should only exist on your backendSanitize all inputs β user inputs are untrusted data from the claiming user
Rate-limit your endpoint independently of BitBadges β your endpoint is publicly addressable
Don't reveal internal details in error messages β they may be shown to the user
Testing
curl
Developer Portal
"Send Test Request" button sends mocked data to your endpoint β verify your handler responds correctly
Claim Tester β test the full claim flow with your plugin included. This is the only place to test unfinalized versions in a real claim context.
Request Bin
Use webhook.site or similar to inspect incoming payloads during development. Set your plugin's endpoint to the request bin URL, trigger a test, and examine exactly what BitBadges sends.
Internal Plugin Architecture
For reference, core (built-in) plugins follow the same pattern internally:
The key takeaway: state updates (toSet) are only applied if the overall claim succeeds. This is the same atomicity guarantee your custom plugins get with BitBadges-managed state presets.
Last updated