{"openapi":"3.1.0","info":{"title":"Confirmations","description":"Multi-tenant blockchain confirmation monitoring service.\n\nUsers sign up by donating to the EFF or subscribing to a paid plan, register their own wallet addresses, and create monitored payments. The server watches EVM chains for incoming transfers and sends webhook callbacks when transactions are confirmed. Confirmations never holds, moves, or has access to user funds.\n\n**Supported chains:** `bitcoin` `ethereum` `base` `arbitrum` `optimism` `polygon` `bnb` `avalanche` `linea` `zksync`\n\n**Authentication:** endpoints marked with a lock require an `X-API-Key` header.\n\n**Amounts:** returned as strings to avoid float precision loss.\n\n**Payment statuses:** `PENDING` → `PAID` | `EXPIRED`","version":"1.0.0"},"paths":{"/signup/options":{"get":{"tags":["public"],"summary":"Get signup options","description":"Returns available donation chains and plan tiers for signup.","operationId":"get_signup_options_signup_options_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SignupOptionsOut"}}}}}}},"/signup":{"post":{"tags":["public"],"summary":"Create account","description":"Sign up via EFF donation or paid plan subscription.\n\n**method=donation**: Creates a signup intent. Donate the exact amount to the EFF address, then poll and claim.\n**method=subscribe**: Subscribes to a paid plan (growth/scale) immediately. Returns API key.","operationId":"signup_signup_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SignupIn"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"400":{"description":"Invalid parameters"},"502":{"description":"Failed to fetch price"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/signup/{intent_id}":{"get":{"tags":["public"],"summary":"Poll donation status","description":"Returns the current status of a signup intent.","operationId":"get_signup_intent_signup__intent_id__get","parameters":[{"name":"intent_id","in":"path","required":true,"schema":{"type":"string","title":"Intent Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IntentStatusOut"}}}},"404":{"description":"Intent not found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/signup/{intent_id}/claim":{"post":{"tags":["public"],"summary":"Claim account after donation","description":"Claim an API key after the EFF donation is confirmed on-chain.","operationId":"claim_signup_signup__intent_id__claim_post","parameters":[{"name":"intent_id","in":"path","required":true,"schema":{"type":"string","title":"Intent Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SignupOut"}}}},"400":{"description":"Donation not yet confirmed"},"404":{"description":"Intent not found"},"409":{"description":"Already claimed"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/plans":{"get":{"tags":["public"],"summary":"List plans","description":"Returns all available subscription plans with their event limits and pricing.","operationId":"list_plans_plans_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/PlanOut"},"type":"array","title":"Response List Plans Plans Get"}}}}}}},"/challenges":{"post":{"tags":["challenges"],"summary":"Create donation challenge","description":"Create a donation challenge. The caller's service gates an action behind this small\ncharitable donation to the EFF. Poll the challenge status or provide a callback URL.","operationId":"create_challenge_challenges_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChallengeIn"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChallengeOut"}}}},"400":{"description":"Invalid parameters"},"502":{"description":"Failed to fetch price"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/challenges/{challenge_id}":{"get":{"tags":["challenges"],"summary":"Get challenge status","description":"Returns the current status of a donation challenge.","operationId":"get_challenge_challenges__challenge_id__get","parameters":[{"name":"challenge_id","in":"path","required":true,"schema":{"type":"string","title":"Challenge Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChallengeStatusOut"}}}},"404":{"description":"Challenge not found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/addresses":{"post":{"tags":["addresses"],"summary":"Register addresses","description":"Register one or more wallet addresses for a chain. Duplicates are idempotent by (user, chain, address).","operationId":"add_addresses_addresses_post","parameters":[{"name":"x-api-key","in":"header","required":true,"schema":{"type":"string","title":"X-Api-Key"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AddAddressesIn"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/AddressOut"},"title":"Response Add Addresses Addresses Post"}}}},"400":{"description":"Unsupported chain"},"401":{"description":"Invalid API key"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"tags":["addresses"],"summary":"List addresses","description":"Returns all wallet addresses registered by the caller.","operationId":"list_addresses_addresses_get","parameters":[{"name":"x-api-key","in":"header","required":true,"schema":{"type":"string","title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/AddressOut"},"title":"Response List Addresses Addresses Get"}}}},"401":{"description":"Invalid API key"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/addresses/{address}":{"delete":{"tags":["addresses"],"summary":"Remove address","description":"Delete an address. Optionally filter by chain to remove a single (chain, address) record.","operationId":"remove_address_addresses__address__delete","parameters":[{"name":"address","in":"path","required":true,"schema":{"type":"string","title":"Address"}},{"name":"chain","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Chain"}},{"name":"x-api-key","in":"header","required":true,"schema":{"type":"string","title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"401":{"description":"Invalid API key"},"404":{"description":"Address not found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/payments":{"post":{"tags":["payments"],"summary":"Create payment","description":"Create a monitored payment intent. Allocates a receive address from the caller's pool,\nconverts the USD amount to an exact native-token amount, and begins monitoring the chain.\nThe callback URL is POSTed with transaction details once the transfer is confirmed.","operationId":"create_payment_payments_post","parameters":[{"name":"x-api-key","in":"header","required":true,"schema":{"type":"string","title":"X-Api-Key"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreatePaymentIn"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreatePaymentOut"}}}},"400":{"description":"Unsupported chain or no addresses registered for that chain"},"401":{"description":"Invalid API key"},"403":{"description":"No active subscription"},"429":{"description":"Monthly event limit reached"},"502":{"description":"Failed to fetch price or reach chain RPC"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"tags":["payments"],"summary":"List payments","description":"Lists all payments for the caller, newest first.","operationId":"list_payments_payments_get","parameters":[{"name":"x-api-key","in":"header","required":true,"schema":{"type":"string","title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/PaymentStatus"},"title":"Response List Payments Payments Get"}}}},"401":{"description":"Invalid API key"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/payments/{payment_id}":{"get":{"tags":["payments"],"summary":"Get payment status","description":"Returns the current status of a single payment owned by the caller.","operationId":"get_payment_payments__payment_id__get","parameters":[{"name":"payment_id","in":"path","required":true,"schema":{"type":"string","title":"Payment Id"}},{"name":"x-api-key","in":"header","required":true,"schema":{"type":"string","title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaymentStatus"}}}},"401":{"description":"Invalid API key"},"404":{"description":"Payment not found"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/account":{"get":{"tags":["account"],"summary":"Get account","description":"Returns the caller's subscription plan, usage, and billing period.","operationId":"get_account_account_get","parameters":[{"name":"x-api-key","in":"header","required":true,"schema":{"type":"string","title":"X-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AccountOut"}}}},"401":{"description":"Invalid API key"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/account/subscribe":{"post":{"tags":["account"],"summary":"Subscribe to plan","description":"Create or change the caller's subscription plan. Plans: starter (free, 100 events/mo), growth ($49, 10k events/mo), scale ($199, 100k events/mo).","operationId":"subscribe_account_subscribe_post","parameters":[{"name":"x-api-key","in":"header","required":true,"schema":{"type":"string","title":"X-Api-Key"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubscribeIn"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AccountOut"}}}},"400":{"description":"Unknown plan"},"401":{"description":"Invalid API key"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}}},"components":{"schemas":{"AccountOut":{"properties":{"user_id":{"type":"string","title":"User Id","examples":["9a7d31d2-98f8-4d2e-a4d1-4f1849320d99"]},"email":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Email","examples":["ops@example.com"]},"plan":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Plan","description":"Current plan name","examples":["starter"]},"status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Status","description":"Subscription status","examples":["active"]},"events_used":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Events Used","description":"Events consumed this billing period","examples":[3]},"monthly_event_limit":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Monthly Event Limit","examples":[100]},"period_end":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Period End","description":"ISO 8601 end of current billing period","examples":["2026-03-25T00:12:54.211472+00:00"]}},"type":"object","required":["user_id","email","plan","status","events_used","monthly_event_limit","period_end"],"title":"AccountOut"},"AddAddressesIn":{"properties":{"chain":{"type":"string","title":"Chain","description":"Target chain: bitcoin, ethereum, base, arbitrum, optimism, polygon, bnb, avalanche, linea, zksync","examples":["ethereum"]},"addresses":{"items":{"type":"string"},"type":"array","title":"Addresses","description":"Wallet addresses to register","examples":[["0xA11CE...","0xB0B..."]]}},"type":"object","required":["chain","addresses"],"title":"AddAddressesIn"},"AddressOut":{"properties":{"chain":{"type":"string","title":"Chain","examples":["ethereum"]},"address":{"type":"string","title":"Address","examples":["0xA11CE..."]}},"type":"object","required":["chain","address"],"title":"AddressOut"},"ChallengeIn":{"properties":{"chain":{"type":"string","title":"Chain","description":"Donation chain: bitcoin, ethereum, or binance","examples":["ethereum"]},"usd_amount":{"anyOf":[{"type":"number","exclusiveMinimum":0.0},{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"}],"title":"Usd Amount","description":"USD amount to donate","examples":["5.00"]},"callback_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Callback Url","description":"Optional URL to receive webhook on confirmation"}},"type":"object","required":["chain","usd_amount"],"title":"ChallengeIn"},"ChallengeOut":{"properties":{"challenge_id":{"type":"string","title":"Challenge Id"},"chain":{"type":"string","title":"Chain"},"address":{"type":"string","title":"Address"},"amount_native":{"type":"string","title":"Amount Native"},"amount_smallest":{"type":"string","title":"Amount Smallest"},"usd_amount":{"type":"string","title":"Usd Amount"},"expires_at":{"type":"string","title":"Expires At"}},"type":"object","required":["challenge_id","chain","address","amount_native","amount_smallest","usd_amount","expires_at"],"title":"ChallengeOut"},"ChallengeStatusOut":{"properties":{"challenge_id":{"type":"string","title":"Challenge Id"},"status":{"type":"string","title":"Status"},"chain":{"type":"string","title":"Chain"},"amount_native":{"type":"string","title":"Amount Native"},"tx_hash":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Tx Hash"},"expires_at":{"type":"string","title":"Expires At"}},"type":"object","required":["challenge_id","status","chain","amount_native","tx_hash","expires_at"],"title":"ChallengeStatusOut"},"CreatePaymentIn":{"properties":{"usd_amount":{"anyOf":[{"type":"number","exclusiveMinimum":0.0},{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"}],"title":"Usd Amount","description":"Payment amount in USD","examples":["25.00"]},"chain":{"type":"string","title":"Chain","description":"Target chain: bitcoin, ethereum, base, arbitrum, optimism, polygon, bnb, avalanche, linea, zksync","examples":["ethereum"]},"callback_url":{"type":"string","title":"Callback Url","description":"URL to receive webhook on confirmation","examples":["https://shop.example/callback"]}},"type":"object","required":["usd_amount","chain","callback_url"],"title":"CreatePaymentIn"},"CreatePaymentOut":{"properties":{"payment_id":{"type":"string","title":"Payment Id","examples":["0f2ac20d-3189-44b6-9fcf-6fb736e0aacf"]},"chain":{"type":"string","title":"Chain","examples":["ethereum"]},"address":{"type":"string","title":"Address","description":"Allocated receive address","examples":["0xA11CE..."]},"amount_wei":{"type":"string","title":"Amount Wei","description":"Exact amount in smallest unit","examples":["10483000000000000"]},"amount_native":{"type":"string","title":"Amount Native","description":"Amount in native token","examples":["0.010483"]},"usd_amount":{"type":"string","title":"Usd Amount","examples":["25.00"]},"expires_at":{"type":"string","title":"Expires At","description":"ISO 8601 expiry timestamp","examples":["2026-02-23T13:45:12.102849+00:00"]}},"type":"object","required":["payment_id","chain","address","amount_wei","amount_native","usd_amount","expires_at"],"title":"CreatePaymentOut"},"HTTPValidationError":{"properties":{"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"type":"array","title":"Detail"}},"type":"object","title":"HTTPValidationError"},"IntentStatusOut":{"properties":{"intent_id":{"type":"string","title":"Intent Id"},"status":{"type":"string","title":"Status"},"chain":{"type":"string","title":"Chain"},"amount_native":{"type":"string","title":"Amount Native"},"tx_hash":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Tx Hash"},"expires_at":{"type":"string","title":"Expires At"}},"type":"object","required":["intent_id","status","chain","amount_native","tx_hash","expires_at"],"title":"IntentStatusOut"},"PaymentStatus":{"properties":{"payment_id":{"type":"string","title":"Payment Id","examples":["0f2ac20d-3189-44b6-9fcf-6fb736e0aacf"]},"status":{"type":"string","title":"Status","description":"PENDING, PAID, or EXPIRED","examples":["PENDING"]},"chain":{"type":"string","title":"Chain","examples":["ethereum"]},"address":{"type":"string","title":"Address","examples":["0xA11CE..."]},"amount_native":{"type":"string","title":"Amount Native","examples":["0.010483"]},"usd_amount":{"type":"string","title":"Usd Amount","examples":["25.00"]},"tx_hash":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Tx Hash","description":"Transaction hash once detected","examples":[null]},"confirmations":{"type":"integer","title":"Confirmations","description":"Number of block confirmations","examples":[0]}},"type":"object","required":["payment_id","status","chain","address","amount_native","usd_amount","tx_hash","confirmations"],"title":"PaymentStatus"},"PlanOut":{"properties":{"name":{"type":"string","title":"Name","examples":["starter"]},"monthly_event_limit":{"type":"integer","title":"Monthly Event Limit","examples":[100]},"monthly_price_usd":{"type":"integer","title":"Monthly Price Usd","examples":[0]}},"type":"object","required":["name","monthly_event_limit","monthly_price_usd"],"title":"PlanOut"},"SignupIn":{"properties":{"method":{"type":"string","enum":["donation","subscribe"],"title":"Method"},"chain":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Chain","description":"Required for donation method"},"usd_amount":{"anyOf":[{"type":"number","exclusiveMinimum":0.0},{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"},{"type":"null"}],"title":"Usd Amount","description":"Required for donation method"},"plan":{"type":"string","title":"Plan","description":"Plan name: starter, growth, or scale","default":"starter"},"email":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Email","description":"Optional contact email"}},"type":"object","required":["method"],"title":"SignupIn"},"SignupOptionsChain":{"properties":{"chain":{"type":"string","title":"Chain"},"address":{"type":"string","title":"Address"},"symbol":{"type":"string","title":"Symbol"},"auto_verify":{"type":"boolean","title":"Auto Verify"}},"type":"object","required":["chain","address","symbol","auto_verify"],"title":"SignupOptionsChain"},"SignupOptionsOut":{"properties":{"min_donation_usd":{"type":"integer","title":"Min Donation Usd"},"chains":{"items":{"$ref":"#/components/schemas/SignupOptionsChain"},"type":"array","title":"Chains"},"plans":{"items":{"$ref":"#/components/schemas/SignupOptionsPlan"},"type":"array","title":"Plans"}},"type":"object","required":["min_donation_usd","chains","plans"],"title":"SignupOptionsOut"},"SignupOptionsPlan":{"properties":{"name":{"type":"string","title":"Name"},"monthly_event_limit":{"type":"integer","title":"Monthly Event Limit"},"monthly_price_usd":{"type":"integer","title":"Monthly Price Usd"},"min_donation_usd":{"type":"integer","title":"Min Donation Usd"}},"type":"object","required":["name","monthly_event_limit","monthly_price_usd","min_donation_usd"],"title":"SignupOptionsPlan"},"SignupOut":{"properties":{"user_id":{"type":"string","title":"User Id","description":"UUID of the new user","examples":["9a7d31d2-98f8-4d2e-a4d1-4f1849320d99"]},"api_key":{"type":"string","title":"Api Key","description":"64-char hex API key. Store securely — cannot be recovered.","examples":["a1b2c3d4e5f6..."]},"plan":{"type":"string","title":"Plan","description":"Subscribed plan name"}},"type":"object","required":["user_id","api_key","plan"],"title":"SignupOut"},"SubscribeIn":{"properties":{"plan":{"type":"string","title":"Plan","description":"Plan name: starter, growth, or scale","examples":["growth"]}},"type":"object","required":["plan"],"title":"SubscribeIn"},"ValidationError":{"properties":{"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"type":"array","title":"Location"},"msg":{"type":"string","title":"Message"},"type":{"type":"string","title":"Error Type"},"input":{"title":"Input"},"ctx":{"type":"object","title":"Context"}},"type":"object","required":["loc","msg","type"],"title":"ValidationError"}}},"tags":[{"name":"public","description":"Health checks, signup, and plan listing. No authentication required."},{"name":"challenges","description":"Standalone donation challenges for anti-abuse gating. No authentication required."},{"name":"addresses","description":"Register and manage wallet addresses for supported chains."},{"name":"payments","description":"Create monitored payment intents and track their status."},{"name":"account","description":"View account details and manage subscription plans."}]}