{"openapi":"3.1.0","info":{"title":"Arcalotl Public API","version":"2026-08","description":"Community-scoped public REST API for subscriptions, plans, members, entitlements, purchases, analytics, checkout links, events, and outbound webhooks.\n\nAuthenticate every request with a bearer API key (`Authorization: Bearer arclt_live_...`). Each key belongs to exactly one community, and every resource is scoped to that community: a valid id owned by another community answers `404 not_found` so ids never leak across tenants.\n\nReads are unconditional. Writes (subscription cancel, plan switches, reactivation, checkout links, external subjects, and webhook-endpoint management) accept an `Idempotency-Key` header so retries are safe. Responses are versioned and additive-only within this `api_version`; new fields may appear without a version bump, so parse leniently.\n\nA breaking change ships as a new version, never as an edit to this one. Before a version, endpoint or field is removed it is announced in advance, and responses that use it carry a `Deprecation` header (RFC 9745) and a `Sunset` header (RFC 8594) with the removal date. Nothing in this version is deprecated. Policy: https://www.arcalotl.com/docs/api/versioning\n\nErrors use RFC 9457 `application/problem+json` with a stable machine-readable `code`. Rate-limit state is returned on every response through the `RateLimit-*` headers.\n"},"servers":[{"url":"https://api.arcalotl.com","description":"Production"}],"security":[{"apiKey":[]}],"tags":[{"name":"Subscriptions","description":"Recurring subscriptions. List and read subscriptions, schedule a period-end cancellation, undo one, and move a subscription between plans."},{"name":"Plans","description":"Plan tiers and their nested billing plans (monthly, annual, one-time). Tiers are the customer-facing offerings; plans are the priced billing options beneath them."},{"name":"Members","description":"Members, their linked platform identities, and the operator-owned external subjects mapped to them. Look a member up by either identity, attach your own user id, or open the member portal for them."},{"name":"Entitlements","description":"Authoritative logical access a member holds. Read effective entitlements and the platform-specific benefits each entitlement confers."},{"name":"Purchases","description":"One-time purchases and their access windows."},{"name":"Analytics","description":"A pragmatic per-community revenue and membership rollup."},{"name":"Checkout Links","description":"Create a real checkout session for a buyer identified by an Arcalotl member ID, an operator-owned external user ID, or a connected-platform identity. Every checkout returns the Arcalotl checkout page URL."},{"name":"Discounts","description":"Community discount codes: value, restrictions, schedule, and redemption counts. Apply a code to a checkout through the checkout-links endpoint."},{"name":"Events","description":"The projected public event feed. Poll newest-first with keyset pagination to reconcile state, or read a single event by id. The same envelopes are delivered to webhook endpoints."},{"name":"Webhook Endpoints","description":"Manage outbound webhook endpoints, inspect their delivery attempts, and browse the machine-readable event-type catalog. Signing secrets are returned exactly once, at creation and on rotation."}],"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","bearerFormat":"arclt_live_","description":"Community-scoped API key. Send it as `Authorization: Bearer arclt_live_...`. Keys carry scopes; endpoints that need a specific scope answer `403 missing_scope` when the key lacks it."}},"parameters":{"cursor":{"name":"cursor","in":"query","required":false,"schema":{"type":"string"},"description":"Opaque keyset cursor copied verbatim from a previous response's `next_cursor`. Omit it to fetch the first page.","example":"eyJpZCI6IjAxSk1aWDQ3In0"},"limit":{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":100,"default":25},"description":"Page size, capped at 100.","example":25},"idempotencyKey":{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string","minLength":1,"maxLength":255},"description":"Opaque client-generated key (1-255 chars) that makes a write safe to retry for 24h. A replay of the same key and body returns the original response with `Idempotent-Replayed: true`. Reusing the key with a different body returns `422 idempotency_key_reuse`; an in-flight duplicate returns `409 request_in_flight`.","example":"6f8a2c1e-4b3d-4e9a-9c21-2f0a1b7d5e44"},"WebhookIdHeader":{"name":"webhook-id","in":"header","required":true,"schema":{"type":"string"},"description":"Unique id of the delivered event (`evt_` prefix). Stable across retries of the same event; deduplicate on it.","example":"evt_9f2c4b1e5a7d4c0f8b3a2d1e6f5c4b3a"},"WebhookTimestampHeader":{"name":"webhook-timestamp","in":"header","required":true,"schema":{"type":"string"},"description":"Unix seconds at send time. Reject deliveries more than 5 minutes old or in the future to prevent replay.","example":"1783468496"},"WebhookSignatureHeader":{"name":"webhook-signature","in":"header","required":true,"schema":{"type":"string"},"description":"Standard Webhooks v1 signature: `v1,` plus the base64 HMAC-SHA256 of `{id}.{timestamp}.{body}`, keyed with the base64-decoded portion of the endpoint secret after the `whsec_` prefix. May carry several space-separated `v1,` tokens during secret rotation; accept the delivery when any one of them verifies.","example":"v1,g0hM9SsE+OTPJTGt/tmIKtSyZlE3uFJELVlNIOLJ1OE="}},"headers":{"RateLimit-Limit":{"description":"Requests allowed per window for the caller's key.","schema":{"type":"integer"},"example":120},"RateLimit-Remaining":{"description":"Burst tokens remaining right now. The limiter is a token bucket: tokens refill continuously toward the burst capacity (60), so this value is always at most the burst size even though the sustained limit is 300 per minute.","schema":{"type":"integer"},"example":42},"RateLimit-Reset":{"description":"Seconds until the rate-limit window fully replenishes.","schema":{"type":"integer"},"example":42},"Retry-After":{"description":"Seconds to wait before retrying, returned on 429.","schema":{"type":"integer"},"example":1},"Idempotent-Replayed":{"description":"Present and `true` when the response is a replay of an earlier request that carried the same `Idempotency-Key`.","schema":{"type":"boolean"},"example":true}},"responses":{"Problem":{"description":"RFC 9457 problem response. `code` is a stable machine-readable identifier; `type` dereferences to the documentation anchor for that code.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimit-Remaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimit-Reset"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"BadRequest":{"description":"A request parameter is malformed: an unreadable body, invalid JSON, a missing required query parameter, or a pagination cursor that is not one returned by a previous response.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://docs.arcalotl.com/api/errors#invalid_request","title":"Bad Request","status":400,"detail":"The 'cursor' parameter is not a valid cursor from a previous response.","code":"invalid_request"}}}},"Unauthorized":{"description":"The API key is missing, malformed, revoked, or unknown. The body is identical for every failure mode so responses cannot reveal key state.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://docs.arcalotl.com/api/errors#unauthorized","title":"Unauthorized","status":401,"detail":"A valid API key is required. Provide it as 'Authorization: Bearer arclt_live_...'.","code":"unauthorized"}}}},"Forbidden":{"description":"The API key is valid but lacks the scope this endpoint requires.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://docs.arcalotl.com/api/errors#missing_scope","title":"Forbidden","status":403,"detail":"This API key is missing the required scope: subscriptions:read.","code":"missing_scope"}}}},"NotFound":{"description":"No such resource in this community. Because every query is scoped to the key's community, an id owned by another community answers the same 404.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://docs.arcalotl.com/api/errors#not_found","title":"Not Found","status":404,"detail":"The requested resource was not found.","code":"not_found"}}}},"Conflict":{"description":"The request conflicts with current state. The `code` says which conflict: `request_in_flight` (a concurrent request holds this Idempotency-Key), `payment_config_inactive` (no active payment provider), or `not_eligible` (the buyer cannot start this checkout).","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://docs.arcalotl.com/api/errors#request_in_flight","title":"Conflict","status":409,"detail":"Another request with this Idempotency-Key is still in flight.","code":"request_in_flight"}}}},"UnprocessableEntity":{"description":"The request was readable but semantically invalid. The `code` says why: `invalid_request` (bad field values), `plan_required` (the tier has several plans), `platform_not_connected`, `endpoint_limit`, or `idempotency_key_reuse` (same key, different body).","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://docs.arcalotl.com/api/errors#invalid_request","title":"Unprocessable Entity","status":422,"detail":"One or more request values are invalid.","code":"invalid_request"}}}},"RateLimited":{"description":"The per-key rate limit was exceeded. Wait for the interval in `Retry-After`, and pace requests using the `RateLimit-*` headers present on every response.","headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"},"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimit-Remaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimit-Reset"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://docs.arcalotl.com/api/errors#rate_limited","title":"Too Many Requests","status":429,"detail":"Rate limit exceeded. Retry after the interval in the Retry-After header.","code":"rate_limited"}}}},"ServiceUnavailable":{"description":"The subsystem behind this endpoint is not configured on this deployment (for example, webhook management without its encryption key).","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"},"example":{"type":"https://docs.arcalotl.com/api/errors#service_unavailable","title":"Service Unavailable","status":503,"detail":"Webhook management is not configured for this deployment.","code":"service_unavailable"}}}}},"schemas":{"Problem":{"type":"object","description":"RFC 9457 problem detail with a stable `code` extension member.","required":["type","title","status","code"],"properties":{"type":{"type":"string","format":"uri","description":"URI that dereferences to the documentation anchor for `code`.","example":"https://docs.arcalotl.com/api/errors#not_found"},"title":{"type":"string","description":"Short, human-readable summary of the problem type.","example":"Not Found"},"status":{"type":"integer","description":"HTTP status code, repeated for convenience.","example":404},"detail":{"type":"string","description":"Human-readable explanation specific to this occurrence.","example":"The requested resource was not found."},"code":{"type":"string","description":"Stable machine-readable error code. Branch on this, not on `title` or `detail`.","enum":["unauthorized","missing_scope","not_found","invalid_request","rate_limited","internal_error","service_unavailable","endpoint_limit","idempotency_key_reuse","request_in_flight","conflict","payment_config_inactive","plan_required","platform_not_connected","not_eligible"],"example":"not_found"}}},"Subscription":{"type":"object","description":"A recurring subscription. Amount, currency, and interval come from the subscription's current billing plan.","required":["id","status","plan_id","member_id","amount_cents","currency","created_at","updated_at"],"properties":{"id":{"type":"string","description":"Subscription id, unique within the community.","example":"sub_01JMZX47H8Q2C9R4T1"},"status":{"type":"string","description":"Lifecycle status. One of active, trialing, past_due, cancelling, cancelled, paused.","example":"active"},"plan_id":{"type":"string","description":"The billing plan currently backing the subscription.","example":"plan_01JMZX3B2K"},"plan_name":{"type":"string","description":"Display name of the billing plan, when it has one.","example":"Monthly"},"tier_id":{"type":"string","description":"The plan tier the plan belongs to.","example":"tier_01JMZX2A9F"},"tier_name":{"type":"string","description":"Display name of the tier, ready for member-facing UI.","example":"VIP"},"member_id":{"type":"string","description":"The member who owns the subscription.","example":"mem_01JMZX1Z7D"},"amount_cents":{"type":"integer","format":"int64","description":"Recurring amount in the currency's minor units.","example":1500},"currency":{"type":"string","description":"ISO 4217 currency code, lower case.","example":"usd"},"interval":{"type":"string","description":"Billing interval of the current plan (month or year).","example":"month"},"current_period_end":{"type":"string","format":"date-time","description":"When the current paid period ends and access lapses if not renewed.","example":"2026-08-01T00:00:00Z"},"cancel_at":{"type":"string","format":"date-time","description":"When a scheduled cancellation takes effect. Present once the subscription is cancelling.","example":"2026-08-01T00:00:00Z"},"paused_until":{"type":"string","format":"date-time","description":"When a paused subscription is due to resume."},"scheduled_plan_id":{"type":"string","description":"The plan a scheduled period-end change (a downgrade) moves to. Present only while such a change is pending.","example":"plan_01JMZX3B2K"},"scheduled_at":{"type":"string","format":"date-time","description":"When the scheduled plan change takes effect.","example":"2026-08-01T00:00:00Z"},"created_at":{"type":"string","format":"date-time","example":"2026-05-01T12:00:00Z"},"updated_at":{"type":"string","format":"date-time","example":"2026-07-01T12:00:00Z"}}},"SubscriptionPage":{"type":"object","description":"One keyset page of subscriptions.","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Subscription"}},"next_cursor":{"type":"string","description":"Cursor for the next page. Absent on the last page. Pass it back as the `cursor` query parameter.","example":"eyJpZCI6InN1Yl8wMUpNWlg0NyJ9"}}},"CancelSubscriptionResponse":{"type":"object","description":"Result of scheduling a period-end cancellation.","required":["status"],"properties":{"status":{"type":"string","description":"Always `cancelling`: the subscription keeps access until its period ends, then transitions to cancelled.","example":"cancelling"}}},"SwitchOption":{"type":"object","description":"One plan a subscription may move to.","required":["plan_id","tier_id","amount_cents","currency","trial_days"],"properties":{"plan_id":{"type":"string","example":"plan_01JMZX3B2K"},"plan_name":{"type":"string","example":"Collective monthly"},"tier_id":{"type":"string","example":"tier_01JMZX2A9F"},"amount_cents":{"type":"integer","format":"int64","example":19900},"currency":{"type":"string","example":"usd"},"interval":{"type":"string","example":"month"},"trial_days":{"type":"integer","example":0}}},"SwitchOptions":{"type":"object","description":"The plans one subscription can switch to, grouped by member-facing action. `same_tier` and `upgrades` apply immediately with proration; `downgrades` are scheduled for the end of the current period.","required":["same_tier","upgrades","downgrades"],"properties":{"same_tier":{"type":"array","items":{"$ref":"#/components/schemas/SwitchOption"}},"upgrades":{"type":"array","items":{"$ref":"#/components/schemas/SwitchOption"}},"downgrades":{"type":"array","items":{"$ref":"#/components/schemas/SwitchOption"}}}},"SwitchSubscriptionRequest":{"type":"object","required":["plan_id"],"properties":{"plan_id":{"type":"string","description":"A `plan_id` from the subscription's switch options.","example":"plan_01JMZX3B2K"}}},"MemberPortalSessionRequest":{"type":"object","properties":{"return_url":{"type":"string","format":"uri","description":"HTTPS page in your app the portal offers as the way back. Optional.","example":"https://app.example.com/settings/billing"}}},"MemberPortalSession":{"type":"object","description":"A short-lived link that opens the member portal as this member.","required":["url","expires_at"],"properties":{"url":{"type":"string","format":"uri","description":"Redirect the signed-in user here. Valid once per session and for ten minutes.","example":"https://www.arcalotl.com/auth/portal/handoff/eyJtIjoibWVtXzAx..."},"expires_at":{"type":"string","format":"date-time","example":"2026-08-01T12:10:00Z"}}},"Plan":{"type":"object","description":"One priced billing option (monthly, annual, one-time) beneath a tier.","required":["id","amount_cents","currency","trial_days"],"properties":{"id":{"type":"string","example":"plan_01JMZX3B2K"},"amount_cents":{"type":"integer","format":"int64","description":"Price in the currency's minor units.","example":1500},"currency":{"type":"string","example":"usd"},"interval":{"type":"string","description":"month or year. Absent for one-time plans.","example":"month"},"trial_days":{"type":"integer","description":"Free-trial length in days, or 0 for no trial.","example":0}}},"PlanTier":{"type":"object","description":"A customer-facing tier with its nested billing plans.","required":["id","name","kind","billing_mode","features","plans"],"properties":{"id":{"type":"string","example":"tier_01JMZX2A9F"},"name":{"type":"string","example":"Supporter"},"description":{"type":"string","example":"Priority support and members-only channels."},"kind":{"type":"string","description":"subscription or one_time.","example":"subscription"},"billing_mode":{"type":"string","description":"How the tier is billed (for example recurring or one_time).","example":"recurring"},"features":{"type":"array","description":"The perk titles as a flat list, kept for clients that predate perks.","items":{"type":"string"},"example":["Members-only channels","Priority support"]},"perks":{"type":"array","description":"What the tier gives members, each a short title with an optional detail.","items":{"$ref":"#/components/schemas/Perk"},"example":[{"title":"Members-only channels","detail":"Two private text channels and one voice channel for members."},{"title":"Priority support"}]},"plans":{"type":"array","items":{"$ref":"#/components/schemas/Plan"}}}},"Perk":{"type":"object","description":"One thing a tier gives its members. The title is always shown in bold; the detail may carry **bold** and *italic* markdown.","required":["title"],"properties":{"title":{"type":"string","maxLength":48,"example":"Members-only channels"},"detail":{"type":"string","maxLength":200,"example":"Two private text channels and one voice channel for members."}}},"PlanTierList":{"type":"object","description":"The community's plan tiers.","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/PlanTier"}}}},"MemberIdentity":{"type":"object","description":"One platform identity linked to a member.","required":["platform","platform_uid"],"properties":{"platform":{"type":"string","description":"Community platform the identity belongs to.","example":"discord"},"platform_uid":{"type":"string","description":"The member's stable id on that platform.","example":"218421075025461248"},"username":{"type":"string","description":"Best-known handle on that platform, when captured.","example":"ari"}}},"Member":{"type":"object","description":"A member with first-party identities and operator-owned external subjects.","required":["id","created_at","identities","external_subjects"],"properties":{"id":{"type":"string","example":"mem_01JMZX1Z7D"},"created_at":{"type":"string","format":"date-time","example":"2026-05-01T12:00:00Z"},"identities":{"type":"array","items":{"$ref":"#/components/schemas/MemberIdentity"}},"external_subjects":{"type":"array","items":{"$ref":"#/components/schemas/ExternalSubject"}}}},"ExternalSubject":{"type":"object","description":"An operator-owned stable identity mapped to this Arcalotl member.","required":["namespace","external_user_id"],"properties":{"namespace":{"type":"string","example":"production"},"external_user_id":{"type":"string","example":"usr_7f921"}}},"MemberPage":{"type":"object","description":"One keyset page of members.","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Member"}},"next_cursor":{"type":"string","example":"eyJpZCI6Im1lbV8wMUpNWlgxWiJ9"}}},"MemberLinkCodeRequest":{"type":"object","required":["intended_platform"],"properties":{"intended_platform":{"type":"string","description":"The first-party platform where the member will redeem the code.","example":"discord"}}},"MemberLinkCode":{"type":"object","required":["code","member_id","intended_platform","expires_at"],"properties":{"code":{"type":"string","description":"One-time code to enter using the platform's link command.","example":"ABC123XY"},"member_id":{"type":"string","example":"mem_01JMZX1Z7D"},"intended_platform":{"type":"string","example":"discord"},"expires_at":{"type":"string","format":"date-time","description":"The deadline for redeeming this code."}}},"Benefit":{"type":"object","description":"One platform-specific action an entitlement confers, such as a role or channel membership.","required":["platform","type","value"],"properties":{"platform":{"type":"string","example":"discord"},"platform_config_id":{"type":"string","description":"The platform configuration the grant resolves against.","example":"pcfg_01JMZX8C4M"},"type":{"type":"string","description":"What the grant confers (for example role or channel).","example":"role"},"value":{"type":"string","description":"The platform-native identifier granted (for example a role id).","example":"998877665544332211"}}},"Entitlement":{"type":"object","description":"The authoritative effective edge between a member and an entitlement definition. Multiple subscription or purchase sources may support it.","required":["id","entitlement_id","key","name","revision","status","sources","benefits"],"properties":{"id":{"type":"string","example":"ment_01JMZX7Q2R"},"entitlement_id":{"type":"string","example":"ent_01JMZX7P4C"},"key":{"type":"string","example":"vip-access"},"name":{"type":"string","example":"VIP access"},"revision":{"type":"integer","format":"int64","example":4},"status":{"type":"string","enum":["active","revoked"]},"granted_at":{"type":"string","format":"date-time"},"revoked_at":{"type":"string","format":"date-time"},"sources":{"type":"array","items":{"$ref":"#/components/schemas/EntitlementSource"}},"benefits":{"type":"array","items":{"$ref":"#/components/schemas/Benefit"}}}},"EntitlementSource":{"type":"object","description":"One billing object contributing to effective access.","required":["type","id","status"],"properties":{"type":{"type":"string","enum":["subscription","purchase"]},"id":{"type":"string"},"status":{"type":"string","enum":["active","revoked"]},"tier_id":{"type":"string"},"plan_id":{"type":"string"},"client_reference_id":{"type":"string"},"valid_until":{"type":"string","format":"date-time"}}},"EntitlementList":{"type":"object","description":"A member's active entitlements.","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Entitlement"}}}},"EntitlementCheck":{"type":"object","description":"The boolean the caller asked for, plus the entitlements that back it. When `tier_id` was supplied, `entitled` reflects that tier specifically.","required":["entitled","entitlements"],"properties":{"entitled":{"type":"boolean","description":"Whether the identity currently holds the requested access.","example":true},"entitlements":{"type":"array","items":{"$ref":"#/components/schemas/Entitlement"}}}},"Purchase":{"type":"object","description":"A one-time purchase.","required":["id","status","plan_id","member_id","amount_cents","currency","created_at"],"properties":{"id":{"type":"string","example":"pur_01JMZX9E7P"},"status":{"type":"string","example":"completed"},"plan_id":{"type":"string","example":"plan_01JMZX3B2K"},"plan_name":{"type":"string","description":"Display name of the billing plan, when it has one.","example":"Lifetime"},"tier_id":{"type":"string","example":"tier_01JMZX2A9F"},"tier_name":{"type":"string","description":"Display name of the tier, ready for member-facing UI.","example":"VIP"},"member_id":{"type":"string","example":"mem_01JMZX1Z7D"},"amount_cents":{"type":"integer","format":"int64","example":4900},"currency":{"type":"string","example":"usd"},"access_expires_at":{"type":"string","format":"date-time","description":"When timed access from the purchase expires, when time-bounded.","example":"2027-05-01T00:00:00Z"},"paid_at":{"type":"string","format":"date-time","example":"2026-05-01T12:00:05Z"},"created_at":{"type":"string","format":"date-time","example":"2026-05-01T12:00:00Z"}}},"PurchasePage":{"type":"object","description":"One keyset page of purchases.","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Purchase"}},"next_cursor":{"type":"string","example":"eyJpZCI6InB1cl8wMUpNWlg5RSJ9"}}},"AnalyticsSummary":{"type":"object","description":"A per-community revenue and membership rollup.","required":["mrr_cents","currency","active_subscribers","recent_signups","recent_cancels"],"properties":{"mrr_cents":{"type":"integer","format":"int64","description":"Monthly recurring revenue in the currency's minor units.","example":128400},"currency":{"type":"string","example":"usd"},"active_subscribers":{"type":"integer","example":214},"recent_signups":{"type":"integer","description":"Subscriptions created in the recent window.","example":18},"recent_cancels":{"type":"integer","description":"Subscriptions cancelled in the recent window.","example":4}}},"Discount":{"type":"object","description":"One community discount code with restrictions and usage.","required":["id","code","value_type","duration","is_active","tier_ids","members","redeemed_count","pending_count","created_at"],"properties":{"id":{"type":"string","format":"uuid"},"code":{"type":"string","description":"The normalized (uppercase) code members enter.","example":"LAUNCH20"},"name":{"type":"string","description":"Creator-facing label; also shown on provider receipts."},"value_type":{"type":"string","enum":["percent","amount"]},"percent_off":{"type":"integer","nullable":true},"amount_off_cents":{"type":"integer","nullable":true},"currency":{"type":"string","description":"Set for fixed-amount discounts; must match plan currency."},"duration":{"type":"string","enum":["once","repeating","forever"],"description":"How long the discount applies on recurring plans."},"duration_months":{"type":"integer","nullable":true},"use_limit":{"type":"integer","nullable":true,"description":"Total redemptions across all members; null is unlimited."},"min_spend_cents":{"type":"integer","nullable":true},"max_spend_cents":{"type":"integer","nullable":true},"starts_at":{"type":"string","format":"date-time","nullable":true},"expires_at":{"type":"string","format":"date-time","nullable":true},"is_active":{"type":"boolean"},"tier_ids":{"type":"array","items":{"type":"string"},"description":"Restricting tiers; empty means all tiers."},"members":{"type":"array","description":"Member allow-list; empty means everyone.","items":{"type":"object","required":["platform","platform_uid"],"properties":{"platform":{"type":"string"},"platform_uid":{"type":"string"},"username":{"type":"string"}}}},"redeemed_count":{"type":"integer"},"pending_count":{"type":"integer","description":"Reservations held by checkouts in progress."},"created_at":{"type":"string","format":"date-time"}}},"CheckoutLinkRequest":{"type":"object","description":"Identifies the tier to sell and the buyer. Use `subject` for an operator-controlled checkout, or `platform` and `platform_uid` for a first-party platform checkout. `plan_id` may be omitted when the tier has exactly one active plan.","required":["tier_id"],"properties":{"tier_id":{"type":"string","description":"The plan tier to sell.","example":"tier_01JMZX2A9F"},"plan_id":{"type":"string","description":"The billing plan under the tier. Optional when the tier has one plan.","example":"plan_01JMZX3B2K"},"platform":{"type":"string","description":"The buyer's community platform.","example":"discord"},"platform_uid":{"type":"string","description":"The buyer's stable id on that platform.","example":"218421075025461248"},"subject":{"$ref":"#/components/schemas/CheckoutSubject"},"client_reference_id":{"type":"string","description":"Operator reference copied to the entitlement source and webhook payload.","example":"order_8Z4P1"},"return_url":{"type":"string","format":"uri","description":"HTTPS page in your app for an operator-controlled checkout. The checkout page links back to it, and after payment the buyer is sent there with `checkout_id=<checkout_id>` appended. Omit it to land the buyer on the Arcalotl return page instead. Never treat reaching it as payment confirmation.","example":"https://community.example.com/billing/complete"},"discount_code":{"type":"string","description":"Optional community discount code to apply to the checkout. Case-insensitive. Invalid or ineligible codes answer `422 discount_invalid`.","example":"LAUNCH20"}}},"CheckoutSubject":{"type":"object","description":"Exactly one of `member_id` or `external` identifies the buyer for an operator-controlled checkout.","properties":{"member_id":{"type":"string","example":"mem_01JMZX1Z7D"},"external":{"$ref":"#/components/schemas/ExternalSubject"}},"oneOf":[{"required":["member_id"]},{"required":["external"]}]},"CheckoutLink":{"type":"object","description":"The created checkout: the Arcalotl checkout page URL for the buyer. Platform checkouts link back to the community; operator-controlled checkouts link back to `return_url` and send the buyer there after payment (or to the Arcalotl return page when it was omitted). `expires_at` is present only when the provider reports a session expiry; the underlying Stripe checkout session otherwise expires per Stripe's defaults (24h).","required":["checkout_id","member_id","url"],"properties":{"checkout_id":{"type":"string","example":"cs_01JMZX9E7P"},"member_id":{"type":"string","description":"Persist this id as the Arcalotl side of your identity mapping.","example":"mem_01JMZX1Z7D"},"url":{"type":"string","format":"uri","description":"The Arcalotl checkout page URL to send the buyer to.","example":"https://arcalotl.com/checkout/eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJwdXIiOiJjaGVja291dF9lbGVtZW50cyJ9.5mXf9pQ2vR7wA1"},"expires_at":{"type":"string","format":"date-time","description":"When the session expires, when reported by the provider.","example":"2026-07-04T12:00:00Z"}}},"PublicEvent":{"type":"object","description":"The versioned event envelope shared by the events feed and webhook request bodies. `data` is the fat public payload built at projection time; its shape depends on `type`.","required":["id","type","timestamp","api_version","data"],"properties":{"id":{"type":"string","description":"Event id, usable as `after_id` to resume a feed.","example":"evt_01JMZXA1B2"},"type":{"type":"string","description":"Public event type from the catalog.","example":"subscription.active"},"timestamp":{"type":"string","format":"date-time","example":"2026-07-01T12:00:00Z"},"api_version":{"type":"string","description":"The public envelope version this event was projected under.","example":"2026-08"},"data":{"type":"object","description":"The event's public payload. Shape varies by `type`.","additionalProperties":true}}},"PublicEventPage":{"type":"object","description":"One keyset page of projected public events, newest first.","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/PublicEvent"}},"next_cursor":{"type":"string","example":"eyJpZCI6ImV2dF8wMUpNWlhBMSJ9"}}},"EventTypeInfo":{"type":"object","description":"One entry in the public event-type catalog.","required":["type","description"],"properties":{"type":{"type":"string","example":"subscription.active"},"description":{"type":"string","example":"A subscription became active."}}},"EventTypeCatalog":{"type":"object","description":"The authoritative catalog of public event types and their descriptions.","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/EventTypeInfo"}}}},"WebhookEndpoint":{"type":"object","description":"A registered outbound webhook endpoint. Secret material is never included here; the signing secret is returned only at creation and on rotation.","required":["id","server_id","url","description","event_types","enabled","consecutive_failures","created_at","updated_at"],"properties":{"id":{"type":"string","example":"whep_01JMZXB3C4"},"server_id":{"type":"string","description":"The community the endpoint belongs to.","example":"srv_01JMZX0000"},"url":{"type":"string","format":"uri","description":"HTTPS URL that receives signed event deliveries.","example":"https://example.com/webhooks/arcalotl"},"description":{"type":"string","example":"Production billing sync"},"event_types":{"type":"array","description":"Event types this endpoint subscribes to. An empty array means all types.","items":{"type":"string"},"example":["subscription.active","subscription.cancelled"]},"enabled":{"type":"boolean","description":"Whether deliveries are currently sent.","example":true},"consecutive_failures":{"type":"integer","description":"Consecutive failed deliveries. The endpoint is auto-disabled after a sustained failure streak.","example":0},"disabled_at":{"type":"string","format":"date-time","description":"When the endpoint was auto-disabled, if it is disabled."},"disabled_reason":{"type":"string","description":"Why the endpoint was disabled, if it is disabled."},"created_at":{"type":"string","format":"date-time","example":"2026-06-01T12:00:00Z"},"updated_at":{"type":"string","format":"date-time","example":"2026-07-01T12:00:00Z"}}},"WebhookEndpointWithSecret":{"allOf":[{"$ref":"#/components/schemas/WebhookEndpoint"},{"type":"object","required":["secret"],"properties":{"secret":{"type":"string","description":"The plaintext signing secret, returned exactly once. Store it now; it cannot be retrieved again. Use it to verify the signature on every delivery.","example":"whsec_9f8e7d6c5b4a3210fedcba9876543210"}}}]},"CreateWebhookEndpointRequest":{"type":"object","required":["url","event_types"],"properties":{"url":{"type":"string","format":"uri","description":"HTTPS URL to deliver events to.","example":"https://example.com/webhooks/arcalotl"},"description":{"type":"string","example":"Production billing sync"},"event_types":{"type":"array","description":"Event types to subscribe to. An empty array subscribes to all types.","items":{"type":"string"},"example":["subscription.active","subscription.cancelled"]}}},"UpdateWebhookEndpointRequest":{"type":"object","description":"Every field is optional; an omitted field leaves the current value unchanged.","properties":{"url":{"type":"string","format":"uri","example":"https://example.com/webhooks/arcalotl"},"description":{"type":"string","example":"Production billing sync"},"event_types":{"type":"array","items":{"type":"string"},"example":["subscription.active"]},"enabled":{"type":"boolean","description":"Re-enable a disabled endpoint, or pause deliveries.","example":true}}},"RotateWebhookSecretResponse":{"type":"object","description":"The new plaintext signing secret, returned exactly once.","required":["secret"],"properties":{"secret":{"type":"string","example":"whsec_0123456789abcdeffedcba9876543210"}}},"WebhookDelivery":{"type":"object","description":"One delivery attempt log for a webhook endpoint.","required":["id","endpoint_id","event_id","event_type","status","attempts","max_attempts","next_attempt_at","created_at","updated_at"],"properties":{"id":{"type":"string","example":"whd_01JMZXC4D5"},"endpoint_id":{"type":"string","example":"whep_01JMZXB3C4"},"event_id":{"type":"string","description":"The event delivered, matching an id from the events feed.","example":"evt_01JMZXA1B2"},"event_type":{"type":"string","description":"The delivered event's type.","example":"subscription.active"},"status":{"type":"string","description":"Delivery status (for example pending, delivered, or failed).","example":"delivered"},"attempts":{"type":"integer","description":"Delivery attempts made so far.","example":1},"max_attempts":{"type":"integer","description":"Attempts before the delivery is abandoned.","example":8},"next_attempt_at":{"type":"string","format":"date-time","description":"When the next retry is scheduled, for pending deliveries.","example":"2026-07-01T12:01:00Z"},"last_status_code":{"type":"integer","description":"HTTP status returned by the endpoint on the last attempt.","example":200},"last_error":{"type":"string","description":"Error from the last failed attempt, when present."},"response_snippet":{"type":"string","description":"Truncated response body from the last attempt, when captured."},"delivered_at":{"type":"string","format":"date-time","description":"When the delivery first succeeded, when it has.","example":"2026-07-01T12:00:01Z"},"created_at":{"type":"string","format":"date-time","example":"2026-07-01T12:00:00Z"},"updated_at":{"type":"string","format":"date-time","example":"2026-07-01T12:00:01Z"}}},"WebhookDeliveryPage":{"type":"object","description":"One keyset page of delivery attempts.","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/WebhookDelivery"}},"next_cursor":{"type":"string","example":"eyJpZCI6IndoZF8wMUpNWlhDNCJ9"}}},"TestWebhookResponse":{"type":"object","description":"The endpoint's HTTP response to a signed test ping.","required":["status_code"],"properties":{"status_code":{"type":"integer","description":"HTTP status code the endpoint returned for the ping.","example":200}}},"WebhookEnvelopeBase":{"type":"object","description":"The envelope wrapping every webhook delivery and every item in `GET /v1/events`. `data` is the full resource snapshot as of projection time plus event-specific fields; re-fetch through the REST API for authoritative state.","required":["id","type","timestamp","api_version","data"],"properties":{"id":{"type":"string","description":"Public event id (`evt_` prefix). Matches the `webhook-id` header and stays stable across delivery retries.","example":"evt_9f2c4b1e5a7d4c0f8b3a2d1e6f5c4b3a"},"type":{"type":"string","description":"Public event type from the catalog.","example":"subscription.active"},"timestamp":{"type":"string","format":"date-time","description":"When the underlying domain event occurred."},"api_version":{"type":"string","description":"Payload schema version.","example":"2026-08"}}},"WebhookRefundInfo":{"type":"object","description":"Refund facts carried by refund events under `data.refund`.","required":["amount_cents","refund_id"],"properties":{"amount_cents":{"type":"integer","format":"int64","description":"Refunded amount in the currency's minor unit.","example":1500},"reason":{"type":"string","description":"Provider refund reason, empty when not supplied.","example":"requested_by_customer"},"refund_id":{"type":"string","description":"Provider refund id.","example":"re_3PqK2xKZ6qsJl0eN1a2b3c4d"},"charge_id":{"type":"string","description":"Provider charge id the refund applies to.","example":"ch_3PqK2xKZ6qsJl0eN1a2b3c4d"}}},"WebhookDisputeInfo":{"type":"object","description":"Dispute facts carried by dispute events under `data.dispute`.","required":["dispute_id"],"properties":{"dispute_id":{"type":"string","description":"Provider dispute id.","example":"dp_1PqK2xKZ6qsJl0eN1a2b3c4d"},"reason":{"type":"string","description":"Provider dispute reason, absent on dispute_won.","example":"fraudulent"},"amount_cents":{"type":"integer","format":"int64","description":"Disputed amount in minor units, absent on dispute_won.","example":1500}}},"WebhookEntitlementData":{"type":"object","description":"Logical member-entitlement transition. It is emitted from the authoritative ledger independently of whether any first-party platform side effect succeeds.","required":["member","entitlement","changes","reason","trigger_event_id"],"properties":{"member":{"$ref":"#/components/schemas/Member"},"entitlement":{"$ref":"#/components/schemas/WebhookMemberEntitlement"},"changes":{"type":"object","required":["added_benefits","removed_benefits"],"properties":{"added_benefits":{"type":"array","items":{"$ref":"#/components/schemas/Benefit"}},"removed_benefits":{"type":"array","items":{"$ref":"#/components/schemas/Benefit"}}}},"reason":{"type":"string","description":"Ledger transition reason, for example activation, cancellation, purchase_expired, plan_switch, member_link, or definition_updated.","example":"activation"},"trigger_event_id":{"type":"string","description":"Internal lifecycle event id that caused this ledger revision."}}},"WebhookMemberEntitlement":{"type":"object","required":["id","entitlement_id","key","name","revision","status","sources"],"properties":{"id":{"type":"string"},"entitlement_id":{"type":"string"},"key":{"type":"string"},"name":{"type":"string"},"revision":{"type":"integer","format":"int64"},"status":{"type":"string","enum":["active","revoked"]},"sources":{"type":"array","items":{"$ref":"#/components/schemas/EntitlementSource"}}}}}},"paths":{"/v1/subscriptions":{"get":{"tags":["Subscriptions"],"summary":"List subscriptions","description":"Returns a keyset page of the community's subscriptions, newest first. Filter by status, plan, tier, or member.","operationId":"listSubscriptions","security":[{"apiKey":[]}],"parameters":[{"$ref":"#/components/parameters/cursor"},{"$ref":"#/components/parameters/limit"},{"name":"status","in":"query","description":"Filter by lifecycle status (for example active or past_due).","schema":{"type":"string"}},{"name":"plan_id","in":"query","description":"Filter by backing billing plan.","schema":{"type":"string"}},{"name":"tier_id","in":"query","description":"Filter by plan tier.","schema":{"type":"string"}},{"name":"member_id","in":"query","description":"Filter by owning member.","schema":{"type":"string"}}],"responses":{"200":{"description":"A page of subscriptions.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimit-Remaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimit-Reset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubscriptionPage"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/subscriptions/{id}":{"get":{"tags":["Subscriptions"],"summary":"Get a subscription","description":"Returns one subscription by id. Unknown or cross-community ids answer 404.","operationId":"getSubscription","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"The subscription.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Subscription"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/subscriptions/{id}/cancel":{"post":{"tags":["Subscriptions"],"summary":"Cancel a subscription at period end","description":"Schedules a period-end cancellation. The member keeps access until the current period ends, then the subscription transitions to cancelled. Idempotent: a subscription already cancelling returns 202 without another provider call. Supply an `Idempotency-Key` header to make retries safe.","operationId":"cancelSubscription","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"$ref":"#/components/parameters/idempotencyKey"}],"responses":{"202":{"description":"Cancellation scheduled.","headers":{"Idempotent-Replayed":{"$ref":"#/components/headers/Idempotent-Replayed"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CancelSubscriptionResponse"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"description":"An in-flight request with the same Idempotency-Key is still running.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"The Idempotency-Key was reused with a different request body.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/subscriptions/{id}/switch-options":{"get":{"tags":["Subscriptions"],"summary":"List the plans a subscription can switch to","description":"Returns the plans this subscription may move to, grouped as `same_tier` (another billing option of the same tier), `upgrades`, and `downgrades`. Only active or trialing subscriptions have options; a cancelling or paused subscription answers `409 conflict`.","operationId":"getSwitchOptions","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"The switch options.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SwitchOptions"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/subscriptions/{id}/switch":{"post":{"tags":["Subscriptions"],"summary":"Switch a subscription to another plan","description":"Moves the subscription to a plan from its switch options. A same-tier change or an upgrade applies immediately with proration and the response shows the new `plan_id`. A downgrade is scheduled for the end of the current period and the response shows it as `scheduled_plan_id` and `scheduled_at`. Entitlement webhooks follow as usual. Supply an `Idempotency-Key` header to make retries safe.","operationId":"switchSubscription","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"$ref":"#/components/parameters/idempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SwitchSubscriptionRequest"}}}},"responses":{"200":{"description":"The subscription after the change.","headers":{"Idempotent-Replayed":{"$ref":"#/components/headers/Idempotent-Replayed"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Subscription"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"description":"The subscription is not in a state that allows this, or an in-flight request with the same Idempotency-Key is still running.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"The request is unprocessable, or the Idempotency-Key was reused with a different request body.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/subscriptions/{id}/cancel-scheduled-switch":{"post":{"tags":["Subscriptions"],"summary":"Drop a scheduled plan change","description":"Cancels a pending period-end plan change so the subscription keeps its current plan. Answers `409 conflict` when nothing is scheduled. Supply an `Idempotency-Key` header to make retries safe.","operationId":"cancelScheduledSwitch","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"$ref":"#/components/parameters/idempotencyKey"}],"responses":{"200":{"description":"The subscription with no scheduled change.","headers":{"Idempotent-Replayed":{"$ref":"#/components/headers/Idempotent-Replayed"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Subscription"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"description":"The subscription is not in a state that allows this, or an in-flight request with the same Idempotency-Key is still running.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"The request is unprocessable, or the Idempotency-Key was reused with a different request body.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/subscriptions/{id}/reactivate":{"post":{"tags":["Subscriptions"],"summary":"Undo a scheduled cancellation","description":"Reactivates a subscription that is scheduled to cancel at period end, so it renews again. Answers `409 conflict` when the subscription is not cancelling. Supply an `Idempotency-Key` header to make retries safe.","operationId":"reactivateSubscription","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"$ref":"#/components/parameters/idempotencyKey"}],"responses":{"200":{"description":"The subscription, active again.","headers":{"Idempotent-Replayed":{"$ref":"#/components/headers/Idempotent-Replayed"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Subscription"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"description":"The subscription is not in a state that allows this, or an in-flight request with the same Idempotency-Key is still running.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"The request is unprocessable, or the Idempotency-Key was reused with a different request body.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/plans":{"get":{"tags":["Plans"],"summary":"List plan tiers","description":"Returns the community's plan tiers, each with its nested billing plans.","operationId":"listPlans","security":[{"apiKey":[]}],"responses":{"200":{"description":"The community's plan tiers with nested billing plans.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlanTierList"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/plans/{tierId}":{"get":{"tags":["Plans"],"summary":"Get a plan tier","description":"Returns one plan tier by id, with its nested billing plans.","operationId":"getPlanTier","security":[{"apiKey":[]}],"parameters":[{"name":"tierId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"The plan tier.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlanTier"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/members":{"get":{"tags":["Members"],"summary":"List members","description":"Returns a keyset page of the community's members and their identities.","operationId":"listMembers","security":[{"apiKey":[]}],"parameters":[{"$ref":"#/components/parameters/cursor"},{"$ref":"#/components/parameters/limit"}],"responses":{"200":{"description":"A page of members.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimit-Remaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimit-Reset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MemberPage"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/members/lookup":{"get":{"tags":["Members"],"summary":"Look up a member by platform identity","description":"Resolves a member from a platform identity (`platform` and `platform_uid`) or from an operator-owned external subject (`namespace` and `external_user_id`). Send exactly one pair.","operationId":"lookupMember","security":[{"apiKey":[]}],"parameters":[{"name":"platform","in":"query","required":false,"description":"The community platform (for example discord). Pair with `platform_uid`.","schema":{"type":"string"}},{"name":"platform_uid","in":"query","required":false,"description":"The member's stable id on that platform.","schema":{"type":"string"}},{"name":"namespace","in":"query","required":false,"description":"Your identity namespace. Pair with `external_user_id`.","schema":{"type":"string"}},{"name":"external_user_id","in":"query","required":false,"description":"Your own user id for the member.","schema":{"type":"string"}}],"responses":{"200":{"description":"The member.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Member"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/members/{id}":{"get":{"tags":["Members"],"summary":"Get a member","description":"Returns one member by id, with its linked platform identities.","operationId":"getMember","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"The member with platform identities.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Member"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/members/{id}/entitlements":{"get":{"tags":["Entitlements"],"summary":"List a member's entitlements","description":"Returns the member's active entitlements, each resolved to its tier, plan, status, and the platform grants it confers.","operationId":"getMemberEntitlements","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"The member's active entitlements.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EntitlementList"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/members/{id}/link-codes":{"post":{"tags":["Members"],"summary":"Create a member link code","description":"Creates a one-time code that links an identity on the intended platform to this Arcalotl member. The code expires after 10 minutes. Creating a new code for the same member and platform invalidates the previous code; letting a code expire does not change the member or their entitlements. Requires the `members:write` scope.","operationId":"createMemberLinkCode","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MemberLinkCodeRequest"}}}},"responses":{"201":{"description":"The latest one-time link code for this member and platform.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MemberLinkCode"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/members/{id}/portal-sessions":{"post":{"tags":["Members"],"summary":"Open the member portal for a member","description":"Mints a short-lived link that signs this member into the Arcalotl member portal, scoped to your community, where they can change plan, cancel, update their card, and see invoices. Redirect your signed-in user to `url`; it expires after ten minutes. Pass `return_url` so the portal offers a way back to your app. Requires the `members:write` scope.","operationId":"createMemberPortalSession","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MemberPortalSessionRequest"}}}},"responses":{"201":{"description":"The portal link.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MemberPortalSession"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/UnprocessableEntity"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/members/{id}/external-subjects":{"post":{"tags":["Members"],"summary":"Attach your user id to a member","description":"Maps an operator-owned `{namespace, external_user_id}` onto an existing member, for example a buyer who first subscribed through a bot and later signed into your app. Each active entitlement is republished as `member.entitlement.updated` carrying the new subject. Re-sending the identical mapping is a no-op; a subject already mapped to another member, or a member that already has a subject in that namespace, answers `409 conflict`. Requires the `members:write` scope.","operationId":"attachMemberExternalSubject","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"$ref":"#/components/parameters/idempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExternalSubject"}}}},"responses":{"201":{"description":"The member with the subject attached.","headers":{"Idempotent-Replayed":{"$ref":"#/components/headers/Idempotent-Replayed"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Member"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/UnprocessableEntity"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/entitlements/check":{"get":{"tags":["Entitlements"],"summary":"Check an entitlement","description":"Answers whether a member currently holds access, and returns the entitlements that back it. Identify the member by a platform identity (`platform` and `platform_uid`) or by an external subject (`namespace` and `external_user_id`). Pass `tier_id` to check a specific tier; omit it to check for any access. An unknown identity is not entitled.","operationId":"checkEntitlement","security":[{"apiKey":[]}],"parameters":[{"name":"platform","in":"query","required":false,"description":"The community platform (for example discord). Pair with `platform_uid`.","schema":{"type":"string"}},{"name":"platform_uid","in":"query","required":false,"description":"The identity's stable id on that platform.","schema":{"type":"string"}},{"name":"namespace","in":"query","required":false,"description":"Your identity namespace. Pair with `external_user_id`.","schema":{"type":"string"}},{"name":"external_user_id","in":"query","required":false,"description":"Your own user id for the member.","schema":{"type":"string"}},{"name":"tier_id","in":"query","required":false,"description":"Restrict the check to a specific plan tier.","schema":{"type":"string"}}],"responses":{"200":{"description":"Whether the identity is entitled, and to what.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EntitlementCheck"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/purchases":{"get":{"tags":["Purchases"],"summary":"List purchases","description":"Returns a keyset page of the community's one-time purchases, newest first.","operationId":"listPurchases","security":[{"apiKey":[]}],"parameters":[{"$ref":"#/components/parameters/cursor"},{"$ref":"#/components/parameters/limit"}],"responses":{"200":{"description":"A page of one-time purchases.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimit-Remaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimit-Reset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PurchasePage"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/purchases/{id}":{"get":{"tags":["Purchases"],"summary":"Get a purchase","description":"Returns one purchase by id.","operationId":"getPurchase","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"The purchase.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Purchase"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/analytics/summary":{"get":{"tags":["Analytics"],"summary":"Get the analytics summary","description":"Returns a pragmatic per-community rollup: monthly recurring revenue, active subscribers, and recent signups and cancels.","operationId":"getAnalyticsSummary","security":[{"apiKey":[]}],"responses":{"200":{"description":"MRR, active subscribers, and recent signups and cancels.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AnalyticsSummary"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/discounts":{"get":{"tags":["Discounts"],"summary":"List discount codes","description":"Lists the community's unarchived discount codes with their value, restrictions, schedule, and redemption counts. Requires the discounts:read scope.","operationId":"listDiscounts","security":[{"apiKey":[]}],"responses":{"200":{"description":"The community's discount codes.","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Discount"}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/checkout-links":{"post":{"tags":["Checkout Links"],"summary":"Create a checkout link","description":"Creates a checkout for either a first-party platform identity or an operator-controlled subject. Both return the Arcalotl checkout page URL. Operator checkouts require no connected community platform and return the canonical Arcalotl `member_id` for the operator's identity map. Closing checkout changes no entitlement state; create a new checkout link if the buyer returns. Supply an `Idempotency-Key` header to make retries safe.","operationId":"createCheckoutLink","security":[{"apiKey":[]}],"parameters":[{"$ref":"#/components/parameters/idempotencyKey"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CheckoutLinkRequest"}}}},"responses":{"201":{"description":"The checkout page URL (and expiry when reported).","headers":{"Idempotent-Replayed":{"$ref":"#/components/headers/Idempotent-Replayed"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CheckoutLink"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"description":"The buyer is not eligible (already subscribed), or an in-flight request with the same Idempotency-Key is still running.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"422":{"description":"The request is unprocessable, for example `plan_required` when the tier has multiple plans, or an Idempotency-Key reused with a different body.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/events":{"get":{"tags":["Events"],"summary":"List projected public events","description":"Polling and reconciliation feed. Ordered newest-first with keyset pagination. Filter by one or more event types, or resume after a known event id. Requires the events:read scope. Events are recorded from the date the public API was enabled for the deployment; earlier activity is not backfilled.","operationId":"listEvents","security":[{"apiKey":[]}],"parameters":[{"name":"type","in":"query","required":false,"description":"One or more public event types. Repeat the parameter or provide a comma-separated list.","schema":{"type":"array","items":{"type":"string"}}},{"name":"after_id","in":"query","required":false,"description":"Return only events created after the given event id.","schema":{"type":"string"}},{"$ref":"#/components/parameters/cursor"},{"$ref":"#/components/parameters/limit"}],"responses":{"200":{"description":"A keyset page of public event envelopes.","headers":{"RateLimit-Limit":{"$ref":"#/components/headers/RateLimit-Limit"},"RateLimit-Remaining":{"$ref":"#/components/headers/RateLimit-Remaining"},"RateLimit-Reset":{"$ref":"#/components/headers/RateLimit-Reset"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicEventPage"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/events/{id}":{"get":{"tags":["Events"],"summary":"Get one public event","description":"Returns a single public event envelope by id.","operationId":"getEvent","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"The public event envelope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicEvent"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/webhooks/event-types":{"get":{"tags":["Webhook Endpoints"],"summary":"List the machine-readable event-type catalog","description":"The authoritative catalog of public event types and their descriptions. Use it to populate `event_types` on an endpoint. Available to any authenticated key.","operationId":"listEventTypes","security":[{"apiKey":[]}],"responses":{"200":{"description":"The event-type catalog.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EventTypeCatalog"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/v1/webhooks/endpoints":{"get":{"tags":["Webhook Endpoints"],"summary":"List webhook endpoints","description":"Returns the community's webhook endpoints. Signing secrets are never included.","operationId":"listWebhookEndpoints","security":[{"apiKey":[]}],"responses":{"200":{"description":"The community's webhook endpoints (no secrets).","content":{"application/json":{"schema":{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/WebhookEndpoint"}}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"503":{"description":"Webhook management is not configured for this deployment.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"post":{"tags":["Webhook Endpoints"],"summary":"Create a webhook endpoint","description":"Registers an endpoint and returns the `whsec_` signing secret exactly once, in the response body. Store it now; it cannot be retrieved again. Requires webhooks:write. Max 5 endpoints per community (`422 endpoint_limit` beyond that).","operationId":"createWebhookEndpoint","security":[{"apiKey":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateWebhookEndpointRequest"}}}},"responses":{"201":{"description":"The created endpoint plus the one-time plaintext secret.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEndpointWithSecret"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"422":{"description":"Validation failed, or the per-community endpoint limit was reached.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"503":{"description":"Webhook management is not configured for this deployment.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/webhooks/endpoints/{id}":{"get":{"tags":["Webhook Endpoints"],"summary":"Get one webhook endpoint","description":"Returns one webhook endpoint by id. The signing secret is never included.","operationId":"getWebhookEndpoint","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"The webhook endpoint.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEndpoint"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"503":{"description":"Webhook management is not configured for this deployment.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"patch":{"tags":["Webhook Endpoints"],"summary":"Update a webhook endpoint","description":"Patches url, description, event_types, or enabled. Omitted fields are left unchanged. Requires webhooks:write.","operationId":"updateWebhookEndpoint","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateWebhookEndpointRequest"}}}},"responses":{"200":{"description":"The updated endpoint.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEndpoint"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"description":"Validation failed.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"503":{"description":"Webhook management is not configured for this deployment.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"delete":{"tags":["Webhook Endpoints"],"summary":"Delete a webhook endpoint","description":"Permanently deletes a webhook endpoint. Requires webhooks:write.","operationId":"deleteWebhookEndpoint","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"Endpoint deleted."},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"503":{"description":"Webhook management is not configured for this deployment.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/webhooks/endpoints/{id}/rotate-secret":{"post":{"tags":["Webhook Endpoints"],"summary":"Rotate a webhook endpoint's signing secret","description":"Generates and returns a new `whsec_` signing secret exactly once, and invalidates the previous one. Store the new secret immediately. Requires webhooks:write.","operationId":"rotateWebhookSecret","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"The new one-time plaintext secret.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RotateWebhookSecretResponse"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"503":{"description":"Webhook management is not configured for this deployment.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/webhooks/endpoints/{id}/deliveries":{"get":{"tags":["Webhook Endpoints"],"summary":"List an endpoint's delivery attempts","description":"Returns a keyset page of the endpoint's delivery attempt logs, newest first. Each log carries the event id and type, status, attempt counts, and the last HTTP result.","operationId":"listWebhookDeliveries","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"$ref":"#/components/parameters/cursor"},{"$ref":"#/components/parameters/limit"}],"responses":{"200":{"description":"A keyset page of delivery attempt logs.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookDeliveryPage"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"503":{"description":"Webhook management is not configured for this deployment.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/webhooks/endpoints/{id}/deliveries/{deliveryId}/redeliver":{"post":{"tags":["Webhook Endpoints"],"summary":"Requeue a delivery for retry","description":"Requeues a past delivery attempt for immediate redelivery. Requires webhooks:write.","operationId":"redeliverWebhookDelivery","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"deliveryId","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"202":{"description":"Delivery requeued."},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"503":{"description":"Webhook management is not configured for this deployment.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/webhooks/endpoints/{id}/test":{"post":{"tags":["Webhook Endpoints"],"summary":"Send a signed test ping","description":"Synchronously delivers a signed ping envelope to the endpoint and returns the HTTP status code it responded with. Rate-limited to 10/min per endpoint. Requires webhooks:write.","operationId":"testWebhookEndpoint","security":[{"apiKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"The endpoint's HTTP status code for the ping.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TestWebhookResponse"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"description":"The endpoint's test rate limit (10/min) or the per-key rate limit was exceeded.","headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"}},"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"Webhook management is not configured for this deployment.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}}},"webhooks":{"subscription.created":{"post":{"operationId":"webhookSubscriptionCreated","summary":"A subscription was created.","description":"A subscription was created. Delivered as a Standard Webhooks signed POST to every enabled endpoint subscribed to this type. Verify the signature before trusting the payload, deduplicate on the id, and re-fetch through the REST API for authoritative state.","parameters":[{"$ref":"#/components/parameters/WebhookIdHeader"},{"$ref":"#/components/parameters/WebhookTimestampHeader"},{"$ref":"#/components/parameters/WebhookSignatureHeader"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/WebhookEnvelopeBase"},{"type":"object","properties":{"type":{"type":"string","const":"subscription.created"},"data":{"$ref":"#/components/schemas/Subscription"}},"required":["data"]}]}}}},"responses":{"2XX":{"description":"Return any 2xx status quickly to acknowledge the delivery. Anything else, or a timeout after 10 seconds, schedules a retry on the fixed backoff schedule until the delivery exhausts."}}}},"subscription.active":{"post":{"operationId":"webhookSubscriptionActive","summary":"A subscription became active.","description":"A subscription became active. Delivered as a Standard Webhooks signed POST to every enabled endpoint subscribed to this type. Verify the signature before trusting the payload, deduplicate on the id, and re-fetch through the REST API for authoritative state.","parameters":[{"$ref":"#/components/parameters/WebhookIdHeader"},{"$ref":"#/components/parameters/WebhookTimestampHeader"},{"$ref":"#/components/parameters/WebhookSignatureHeader"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/WebhookEnvelopeBase"},{"type":"object","properties":{"type":{"type":"string","const":"subscription.active"},"data":{"allOf":[{"$ref":"#/components/schemas/Subscription"},{"type":"object","properties":{"reconciled":{"type":"boolean","description":"True when this activation came from reconciliation rather than a live provider webhook."}},"required":["reconciled"]}]}},"required":["data"]}]}}}},"responses":{"2XX":{"description":"Return any 2xx status quickly to acknowledge the delivery. Anything else, or a timeout after 10 seconds, schedules a retry on the fixed backoff schedule until the delivery exhausts."}}}},"subscription.renewed":{"post":{"operationId":"webhookSubscriptionRenewed","summary":"A recurring-cycle invoice was paid for an active subscription.","description":"A recurring-cycle invoice was paid for an active subscription. Delivered as a Standard Webhooks signed POST to every enabled endpoint subscribed to this type. Verify the signature before trusting the payload, deduplicate on the id, and re-fetch through the REST API for authoritative state.","parameters":[{"$ref":"#/components/parameters/WebhookIdHeader"},{"$ref":"#/components/parameters/WebhookTimestampHeader"},{"$ref":"#/components/parameters/WebhookSignatureHeader"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/WebhookEnvelopeBase"},{"type":"object","properties":{"type":{"type":"string","const":"subscription.renewed"},"data":{"allOf":[{"$ref":"#/components/schemas/Subscription"},{"type":"object","properties":{"invoice_id":{"type":"string","description":"Provider invoice id of the paid cycle. Renewals deduplicate on it."},"period_end":{"type":"string","format":"date-time","description":"Current period end as of projection time."}},"required":["invoice_id"]}]}},"required":["data"]}]}}}},"responses":{"2XX":{"description":"Return any 2xx status quickly to acknowledge the delivery. Anything else, or a timeout after 10 seconds, schedules a retry on the fixed backoff schedule until the delivery exhausts."}}}},"subscription.past_due":{"post":{"operationId":"webhookSubscriptionPastDue","summary":"A subscription payment failed and the subscription is past due.","description":"A subscription payment failed and the subscription is past due. Delivered as a Standard Webhooks signed POST to every enabled endpoint subscribed to this type. Verify the signature before trusting the payload, deduplicate on the id, and re-fetch through the REST API for authoritative state.","parameters":[{"$ref":"#/components/parameters/WebhookIdHeader"},{"$ref":"#/components/parameters/WebhookTimestampHeader"},{"$ref":"#/components/parameters/WebhookSignatureHeader"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/WebhookEnvelopeBase"},{"type":"object","properties":{"type":{"type":"string","const":"subscription.past_due"},"data":{"allOf":[{"$ref":"#/components/schemas/Subscription"},{"type":"object","properties":{"failure_code":{"type":"string","description":"Provider failure code for the declined payment."},"attempt_count":{"type":"integer","description":"How many collection attempts have failed so far."}}}]}},"required":["data"]}]}}}},"responses":{"2XX":{"description":"Return any 2xx status quickly to acknowledge the delivery. Anything else, or a timeout after 10 seconds, schedules a retry on the fixed backoff schedule until the delivery exhausts."}}}},"subscription.recovered":{"post":{"operationId":"webhookSubscriptionRecovered","summary":"A past-due subscription recovered after a successful payment.","description":"A past-due subscription recovered after a successful payment. Delivered as a Standard Webhooks signed POST to every enabled endpoint subscribed to this type. Verify the signature before trusting the payload, deduplicate on the id, and re-fetch through the REST API for authoritative state.","parameters":[{"$ref":"#/components/parameters/WebhookIdHeader"},{"$ref":"#/components/parameters/WebhookTimestampHeader"},{"$ref":"#/components/parameters/WebhookSignatureHeader"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/WebhookEnvelopeBase"},{"type":"object","properties":{"type":{"type":"string","const":"subscription.recovered"},"data":{"$ref":"#/components/schemas/Subscription"}},"required":["data"]}]}}}},"responses":{"2XX":{"description":"Return any 2xx status quickly to acknowledge the delivery. Anything else, or a timeout after 10 seconds, schedules a retry on the fixed backoff schedule until the delivery exhausts."}}}},"subscription.cancelling":{"post":{"operationId":"webhookSubscriptionCancelling","summary":"A subscription was scheduled to cancel at period end.","description":"A subscription was scheduled to cancel at period end. Delivered as a Standard Webhooks signed POST to every enabled endpoint subscribed to this type. Verify the signature before trusting the payload, deduplicate on the id, and re-fetch through the REST API for authoritative state.","parameters":[{"$ref":"#/components/parameters/WebhookIdHeader"},{"$ref":"#/components/parameters/WebhookTimestampHeader"},{"$ref":"#/components/parameters/WebhookSignatureHeader"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/WebhookEnvelopeBase"},{"type":"object","properties":{"type":{"type":"string","const":"subscription.cancelling"},"data":{"allOf":[{"$ref":"#/components/schemas/Subscription"},{"type":"object","properties":{"reason":{"type":"string","description":"Cancellation reason recorded with the schedule."}}}]}},"required":["data"]}]}}}},"responses":{"2XX":{"description":"Return any 2xx status quickly to acknowledge the delivery. Anything else, or a timeout after 10 seconds, schedules a retry on the fixed backoff schedule until the delivery exhausts."}}}},"subscription.cancelled":{"post":{"operationId":"webhookSubscriptionCancelled","summary":"A subscription was fully cancelled.","description":"A subscription was fully cancelled. Delivered as a Standard Webhooks signed POST to every enabled endpoint subscribed to this type. Verify the signature before trusting the payload, deduplicate on the id, and re-fetch through the REST API for authoritative state.","parameters":[{"$ref":"#/components/parameters/WebhookIdHeader"},{"$ref":"#/components/parameters/WebhookTimestampHeader"},{"$ref":"#/components/parameters/WebhookSignatureHeader"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/WebhookEnvelopeBase"},{"type":"object","properties":{"type":{"type":"string","const":"subscription.cancelled"},"data":{"allOf":[{"$ref":"#/components/schemas/Subscription"},{"type":"object","properties":{"was_saved":{"type":"boolean","description":"True when a retention flow saved this subscription at least once before it cancelled."},"reason":{"type":"string","description":"Recorded cancellation reason, present when known."}},"required":["was_saved"]}]}},"required":["data"]}]}}}},"responses":{"2XX":{"description":"Return any 2xx status quickly to acknowledge the delivery. Anything else, or a timeout after 10 seconds, schedules a retry on the fixed backoff schedule until the delivery exhausts."}}}},"subscription.paused":{"post":{"operationId":"webhookSubscriptionPaused","summary":"A subscription was paused.","description":"A subscription was paused. Delivered as a Standard Webhooks signed POST to every enabled endpoint subscribed to this type. Verify the signature before trusting the payload, deduplicate on the id, and re-fetch through the REST API for authoritative state.","parameters":[{"$ref":"#/components/parameters/WebhookIdHeader"},{"$ref":"#/components/parameters/WebhookTimestampHeader"},{"$ref":"#/components/parameters/WebhookSignatureHeader"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/WebhookEnvelopeBase"},{"type":"object","properties":{"type":{"type":"string","const":"subscription.paused"},"data":{"allOf":[{"$ref":"#/components/schemas/Subscription"},{"type":"object","properties":{"paused_until":{"type":"string","format":"date-time","description":"When the pause ends."}}}]}},"required":["data"]}]}}}},"responses":{"2XX":{"description":"Return any 2xx status quickly to acknowledge the delivery. Anything else, or a timeout after 10 seconds, schedules a retry on the fixed backoff schedule until the delivery exhausts."}}}},"subscription.resumed":{"post":{"operationId":"webhookSubscriptionResumed","summary":"A paused subscription resumed.","description":"A paused subscription resumed. Delivered as a Standard Webhooks signed POST to every enabled endpoint subscribed to this type. Verify the signature before trusting the payload, deduplicate on the id, and re-fetch through the REST API for authoritative state.","parameters":[{"$ref":"#/components/parameters/WebhookIdHeader"},{"$ref":"#/components/parameters/WebhookTimestampHeader"},{"$ref":"#/components/parameters/WebhookSignatureHeader"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/WebhookEnvelopeBase"},{"type":"object","properties":{"type":{"type":"string","const":"subscription.resumed"},"data":{"$ref":"#/components/schemas/Subscription"}},"required":["data"]}]}}}},"responses":{"2XX":{"description":"Return any 2xx status quickly to acknowledge the delivery. Anything else, or a timeout after 10 seconds, schedules a retry on the fixed backoff schedule until the delivery exhausts."}}}},"subscription.trial_started":{"post":{"operationId":"webhookSubscriptionTrialStarted","summary":"A subscription started a free trial.","description":"A subscription started a free trial. Delivered as a Standard Webhooks signed POST to every enabled endpoint subscribed to this type. Verify the signature before trusting the payload, deduplicate on the id, and re-fetch through the REST API for authoritative state.","parameters":[{"$ref":"#/components/parameters/WebhookIdHeader"},{"$ref":"#/components/parameters/WebhookTimestampHeader"},{"$ref":"#/components/parameters/WebhookSignatureHeader"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/WebhookEnvelopeBase"},{"type":"object","properties":{"type":{"type":"string","const":"subscription.trial_started"},"data":{"$ref":"#/components/schemas/Subscription"}},"required":["data"]}]}}}},"responses":{"2XX":{"description":"Return any 2xx status quickly to acknowledge the delivery. Anything else, or a timeout after 10 seconds, schedules a retry on the fixed backoff schedule until the delivery exhausts."}}}},"subscription.trial_will_end":{"post":{"operationId":"webhookSubscriptionTrialWillEnd","summary":"A subscription trial is about to end.","description":"A subscription trial is about to end. Delivered as a Standard Webhooks signed POST to every enabled endpoint subscribed to this type. Verify the signature before trusting the payload, deduplicate on the id, and re-fetch through the REST API for authoritative state.","parameters":[{"$ref":"#/components/parameters/WebhookIdHeader"},{"$ref":"#/components/parameters/WebhookTimestampHeader"},{"$ref":"#/components/parameters/WebhookSignatureHeader"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/WebhookEnvelopeBase"},{"type":"object","properties":{"type":{"type":"string","const":"subscription.trial_will_end"},"data":{"allOf":[{"$ref":"#/components/schemas/Subscription"},{"type":"object","properties":{"trial_end":{"type":"string","format":"date-time","description":"When the trial ends."}}}]}},"required":["data"]}]}}}},"responses":{"2XX":{"description":"Return any 2xx status quickly to acknowledge the delivery. Anything else, or a timeout after 10 seconds, schedules a retry on the fixed backoff schedule until the delivery exhausts."}}}},"subscription.plan_changed":{"post":{"operationId":"webhookSubscriptionPlanChanged","summary":"A subscription's plan changed.","description":"A subscription's plan changed. Delivered as a Standard Webhooks signed POST to every enabled endpoint subscribed to this type. Verify the signature before trusting the payload, deduplicate on the id, and re-fetch through the REST API for authoritative state.","parameters":[{"$ref":"#/components/parameters/WebhookIdHeader"},{"$ref":"#/components/parameters/WebhookTimestampHeader"},{"$ref":"#/components/parameters/WebhookSignatureHeader"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/WebhookEnvelopeBase"},{"type":"object","properties":{"type":{"type":"string","const":"subscription.plan_changed"},"data":{"allOf":[{"$ref":"#/components/schemas/Subscription"},{"type":"object","properties":{"previous_plan_id":{"type":"string","description":"Plan id before the change."},"source":{"type":"string","enum":["provider","member"],"description":"member for a member-managed switch, provider for a change first observed on the payment provider."}},"required":["previous_plan_id","source"]}]}},"required":["data"]}]}}}},"responses":{"2XX":{"description":"Return any 2xx status quickly to acknowledge the delivery. Anything else, or a timeout after 10 seconds, schedules a retry on the fixed backoff schedule until the delivery exhausts."}}}},"subscription.refunded":{"post":{"operationId":"webhookSubscriptionRefunded","summary":"A subscription charge was refunded.","description":"A subscription charge was refunded. Delivered as a Standard Webhooks signed POST to every enabled endpoint subscribed to this type. Verify the signature before trusting the payload, deduplicate on the id, and re-fetch through the REST API for authoritative state.","parameters":[{"$ref":"#/components/parameters/WebhookIdHeader"},{"$ref":"#/components/parameters/WebhookTimestampHeader"},{"$ref":"#/components/parameters/WebhookSignatureHeader"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/WebhookEnvelopeBase"},{"type":"object","properties":{"type":{"type":"string","const":"subscription.refunded"},"data":{"allOf":[{"$ref":"#/components/schemas/Subscription"},{"type":"object","properties":{"refund":{"$ref":"#/components/schemas/WebhookRefundInfo"}},"required":["refund"]}]}},"required":["data"]}]}}}},"responses":{"2XX":{"description":"Return any 2xx status quickly to acknowledge the delivery. Anything else, or a timeout after 10 seconds, schedules a retry on the fixed backoff schedule until the delivery exhausts."}}}},"subscription.disputed":{"post":{"operationId":"webhookSubscriptionDisputed","summary":"A dispute was opened against a subscription charge.","description":"A dispute was opened against a subscription charge. Delivered as a Standard Webhooks signed POST to every enabled endpoint subscribed to this type. Verify the signature before trusting the payload, deduplicate on the id, and re-fetch through the REST API for authoritative state.","parameters":[{"$ref":"#/components/parameters/WebhookIdHeader"},{"$ref":"#/components/parameters/WebhookTimestampHeader"},{"$ref":"#/components/parameters/WebhookSignatureHeader"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/WebhookEnvelopeBase"},{"type":"object","properties":{"type":{"type":"string","const":"subscription.disputed"},"data":{"allOf":[{"$ref":"#/components/schemas/Subscription"},{"type":"object","properties":{"dispute":{"$ref":"#/components/schemas/WebhookDisputeInfo"}},"required":["dispute"]}]}},"required":["data"]}]}}}},"responses":{"2XX":{"description":"Return any 2xx status quickly to acknowledge the delivery. Anything else, or a timeout after 10 seconds, schedules a retry on the fixed backoff schedule until the delivery exhausts."}}}},"subscription.dispute_won":{"post":{"operationId":"webhookSubscriptionDisputeWon","summary":"A subscription charge dispute closed in the merchant's favor.","description":"A subscription charge dispute closed in the merchant's favor. Delivered as a Standard Webhooks signed POST to every enabled endpoint subscribed to this type. Verify the signature before trusting the payload, deduplicate on the id, and re-fetch through the REST API for authoritative state.","parameters":[{"$ref":"#/components/parameters/WebhookIdHeader"},{"$ref":"#/components/parameters/WebhookTimestampHeader"},{"$ref":"#/components/parameters/WebhookSignatureHeader"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/WebhookEnvelopeBase"},{"type":"object","properties":{"type":{"type":"string","const":"subscription.dispute_won"},"data":{"allOf":[{"$ref":"#/components/schemas/Subscription"},{"type":"object","properties":{"dispute":{"$ref":"#/components/schemas/WebhookDisputeInfo"}},"required":["dispute"]}]}},"required":["data"]}]}}}},"responses":{"2XX":{"description":"Return any 2xx status quickly to acknowledge the delivery. Anything else, or a timeout after 10 seconds, schedules a retry on the fixed backoff schedule until the delivery exhausts."}}}},"purchase.completed":{"post":{"operationId":"webhookPurchaseCompleted","summary":"A one-time purchase completed.","description":"A one-time purchase completed. Delivered as a Standard Webhooks signed POST to every enabled endpoint subscribed to this type. Verify the signature before trusting the payload, deduplicate on the id, and re-fetch through the REST API for authoritative state.","parameters":[{"$ref":"#/components/parameters/WebhookIdHeader"},{"$ref":"#/components/parameters/WebhookTimestampHeader"},{"$ref":"#/components/parameters/WebhookSignatureHeader"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/WebhookEnvelopeBase"},{"type":"object","properties":{"type":{"type":"string","const":"purchase.completed"},"data":{"$ref":"#/components/schemas/Purchase"}},"required":["data"]}]}}}},"responses":{"2XX":{"description":"Return any 2xx status quickly to acknowledge the delivery. Anything else, or a timeout after 10 seconds, schedules a retry on the fixed backoff schedule until the delivery exhausts."}}}},"purchase.expired":{"post":{"operationId":"webhookPurchaseExpired","summary":"A timed one-time purchase's access expired.","description":"A timed one-time purchase's access expired. Delivered as a Standard Webhooks signed POST to every enabled endpoint subscribed to this type. Verify the signature before trusting the payload, deduplicate on the id, and re-fetch through the REST API for authoritative state.","parameters":[{"$ref":"#/components/parameters/WebhookIdHeader"},{"$ref":"#/components/parameters/WebhookTimestampHeader"},{"$ref":"#/components/parameters/WebhookSignatureHeader"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/WebhookEnvelopeBase"},{"type":"object","properties":{"type":{"type":"string","const":"purchase.expired"},"data":{"$ref":"#/components/schemas/Purchase"}},"required":["data"]}]}}}},"responses":{"2XX":{"description":"Return any 2xx status quickly to acknowledge the delivery. Anything else, or a timeout after 10 seconds, schedules a retry on the fixed backoff schedule until the delivery exhausts."}}}},"purchase.refunded":{"post":{"operationId":"webhookPurchaseRefunded","summary":"A one-time purchase was refunded.","description":"A one-time purchase was refunded. Delivered as a Standard Webhooks signed POST to every enabled endpoint subscribed to this type. Verify the signature before trusting the payload, deduplicate on the id, and re-fetch through the REST API for authoritative state.","parameters":[{"$ref":"#/components/parameters/WebhookIdHeader"},{"$ref":"#/components/parameters/WebhookTimestampHeader"},{"$ref":"#/components/parameters/WebhookSignatureHeader"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/WebhookEnvelopeBase"},{"type":"object","properties":{"type":{"type":"string","const":"purchase.refunded"},"data":{"allOf":[{"$ref":"#/components/schemas/Purchase"},{"type":"object","properties":{"refund":{"$ref":"#/components/schemas/WebhookRefundInfo"}},"required":["refund"]}]}},"required":["data"]}]}}}},"responses":{"2XX":{"description":"Return any 2xx status quickly to acknowledge the delivery. Anything else, or a timeout after 10 seconds, schedules a retry on the fixed backoff schedule until the delivery exhausts."}}}},"purchase.disputed":{"post":{"operationId":"webhookPurchaseDisputed","summary":"A dispute was opened against a one-time purchase charge.","description":"A dispute was opened against a one-time purchase charge. Delivered as a Standard Webhooks signed POST to every enabled endpoint subscribed to this type. Verify the signature before trusting the payload, deduplicate on the id, and re-fetch through the REST API for authoritative state.","parameters":[{"$ref":"#/components/parameters/WebhookIdHeader"},{"$ref":"#/components/parameters/WebhookTimestampHeader"},{"$ref":"#/components/parameters/WebhookSignatureHeader"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/WebhookEnvelopeBase"},{"type":"object","properties":{"type":{"type":"string","const":"purchase.disputed"},"data":{"allOf":[{"$ref":"#/components/schemas/Purchase"},{"type":"object","properties":{"dispute":{"$ref":"#/components/schemas/WebhookDisputeInfo"}},"required":["dispute"]}]}},"required":["data"]}]}}}},"responses":{"2XX":{"description":"Return any 2xx status quickly to acknowledge the delivery. Anything else, or a timeout after 10 seconds, schedules a retry on the fixed backoff schedule until the delivery exhausts."}}}},"purchase.dispute_won":{"post":{"operationId":"webhookPurchaseDisputeWon","summary":"A one-time purchase dispute closed in the merchant's favor.","description":"A one-time purchase dispute closed in the merchant's favor. Delivered as a Standard Webhooks signed POST to every enabled endpoint subscribed to this type. Verify the signature before trusting the payload, deduplicate on the id, and re-fetch through the REST API for authoritative state.","parameters":[{"$ref":"#/components/parameters/WebhookIdHeader"},{"$ref":"#/components/parameters/WebhookTimestampHeader"},{"$ref":"#/components/parameters/WebhookSignatureHeader"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/WebhookEnvelopeBase"},{"type":"object","properties":{"type":{"type":"string","const":"purchase.dispute_won"},"data":{"allOf":[{"$ref":"#/components/schemas/Purchase"},{"type":"object","properties":{"dispute":{"$ref":"#/components/schemas/WebhookDisputeInfo"}},"required":["dispute"]}]}},"required":["data"]}]}}}},"responses":{"2XX":{"description":"Return any 2xx status quickly to acknowledge the delivery. Anything else, or a timeout after 10 seconds, schedules a retry on the fixed backoff schedule until the delivery exhausts."}}}},"member.entitlement.granted":{"post":{"operationId":"webhookMemberEntitlementGranted","summary":"A member gained an effective entitlement.","description":"A member gained an effective logical entitlement. Delivered as a Standard Webhooks signed POST to every enabled endpoint subscribed to this type. Verify the signature before trusting the payload, deduplicate on the id, and re-fetch through the REST API for authoritative state.","parameters":[{"$ref":"#/components/parameters/WebhookIdHeader"},{"$ref":"#/components/parameters/WebhookTimestampHeader"},{"$ref":"#/components/parameters/WebhookSignatureHeader"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/WebhookEnvelopeBase"},{"type":"object","properties":{"type":{"type":"string","const":"member.entitlement.granted"},"data":{"$ref":"#/components/schemas/WebhookEntitlementData"}},"required":["data"]}]}}}},"responses":{"2XX":{"description":"Return any 2xx status quickly to acknowledge the delivery. Anything else, or a timeout after 10 seconds, schedules a retry on the fixed backoff schedule until the delivery exhausts."}}}},"member.entitlement.updated":{"post":{"operationId":"webhookMemberEntitlementUpdated","summary":"A member entitlement changed while its effective status remained the same.","description":"Sources, benefits, or linked identities changed without a grant or revoke edge transition. Verify the Standard Webhooks signature, deduplicate on the envelope id, and apply only revisions newer than the one you stored.","parameters":[{"$ref":"#/components/parameters/WebhookIdHeader"},{"$ref":"#/components/parameters/WebhookTimestampHeader"},{"$ref":"#/components/parameters/WebhookSignatureHeader"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/WebhookEnvelopeBase"},{"type":"object","properties":{"type":{"type":"string","const":"member.entitlement.updated"},"data":{"$ref":"#/components/schemas/WebhookEntitlementData"}},"required":["data"]}]}}}},"responses":{"2XX":{"description":"Return any 2xx status quickly to acknowledge the delivery."}}}},"member.entitlement.revoked":{"post":{"operationId":"webhookMemberEntitlementRevoked","summary":"A member lost an effective entitlement.","description":"A member lost an effective logical entitlement. Delivered as a Standard Webhooks signed POST to every enabled endpoint subscribed to this type. Verify the signature before trusting the payload, deduplicate on the id, and re-fetch through the REST API for authoritative state.","parameters":[{"$ref":"#/components/parameters/WebhookIdHeader"},{"$ref":"#/components/parameters/WebhookTimestampHeader"},{"$ref":"#/components/parameters/WebhookSignatureHeader"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/WebhookEnvelopeBase"},{"type":"object","properties":{"type":{"type":"string","const":"member.entitlement.revoked"},"data":{"$ref":"#/components/schemas/WebhookEntitlementData"}},"required":["data"]}]}}}},"responses":{"2XX":{"description":"Return any 2xx status quickly to acknowledge the delivery. Anything else, or a timeout after 10 seconds, schedules a retry on the fixed backoff schedule until the delivery exhausts."}}}}}}