{
  "openapi": "3.1.0",
  "info": {
    "title": "Confirmo Subscribe API",
    "version": "3.0.0",
    "description": "Confirmo's subscription API. Create and manage subscription plans, track\nsubscriptions and payments.\n\nAuthenticate every request with a bearer API key from your Confirmo\ndashboard. Your account scope is determined by the key — you never\nsupply your merchant ID on individual requests.\n"
  },
  "servers": [
    {
      "url": "https://api.confirmo.com"
    }
  ],
  "security": [
    {
      "bearerAuth": []
    }
  ],
  "tags": [
    {
      "name": "Plans",
      "description": "Subscription plans you offer to your subscribers."
    },
    {
      "name": "Subscriptions",
      "description": "Your subscribers' active and past subscriptions."
    },
    {
      "name": "Payments",
      "description": "Recurring-billing payment records."
    },
    {
      "name": "Webhooks",
      "description": "Confirmo notifies you of subscription and payment events by POSTing a\nsigned JSON event to the `notifyUrl` you set when creating a\nsubscription. Every event has the same envelope (see `WebhookEnvelope`);\n`data` carries the affected resource — a `Subscription` for lifecycle\nevents, a `Payment` for payment events — in the same shape the REST API\nreturns it. A request carries the event in the body and repeats its\nrouting fields as headers:\n\n```http\nPOST /confirmo/webhook HTTP/1.1\ncontent-type: application/json\nuser-agent: Confirmo-Webhook-Dispatcher/1.0 (+https://confirmo.net)\nwebhook-id: 019807c2-6f30-7c5a-b0e4-3d81f2a6c910\nwebhook-timestamp: 1780041601\nwebhook-signature: v1a,LWOC1Nk/u/TrGz70da941IK1AvVMk5uD0/lJTNDco8yYelqMtTmNTxpLpe+BzrPDvkTzmNhJ27nf6pXdvZ+/gA==\nwebhook-type: subscription.created\nwebhook-sequence: 1\n```\n\n**Delivery.** At-least-once. Return any `2xx` to acknowledge receipt:\nyou have 5 seconds to connect and return the status line, plus a further\n20 seconds to finish the response body. A `5xx`, `408`, `425`, `429`, a\ntimeout, a connection error, or a redirect we don't follow is a failed\nattempt and is retried after `5s, 5m, 30m, 2h, 5h, 8h, 8h` — 8 attempts\nspanning roughly 24 hours, after which the event is abandoned. Any other\n`4xx` is a permanent rejection: we give up immediately without retrying.\nOnly `307` and `308` redirects are followed, at most one hop.\n\n**Idempotency.** A retried attempt repeats the same `id` (and\n`webhook-id`), so deduping on `id` is enough to survive retries. A\nsupport-initiated replay of a past event carries a *new* `id` with the\noriginal `timestamp`, `sequence` and `data`, so gate state changes on\n(`resourceId`, `sequence`) rather than on `id` alone.\n\n**Ordering.** `sequence` increases monotonically per `resourceId`;\npayment events share their parent subscription's counter, so a single\nversion-gate per subscription orders everything you receive about it.\nApply an event only when its `sequence` is higher than the last one you\napplied for that `resourceId`. Events for different subscriptions are\nunordered relative to one another, and the sequence is not guaranteed\ngap-free — a permanently rejected delivery leaves a gap.\n\n**Verification.** Every request is signed with Ed25519 per the\n[Standard Webhooks](https://www.standardwebhooks.com) specification:\n\n1. Reject the request if `webhook-timestamp` — Unix seconds, stamped\n   when the attempt was signed, *not* the event's `timestamp` — is more\n   than 5 minutes away from your clock.\n2. Build the signed message as\n   `{webhook-id}.{webhook-timestamp}.{raw request body}`, UTF-8 encoded,\n   using the body bytes exactly as received. Do not parse and\n   re-serialize the JSON first — that breaks the signature.\n3. Split `webhook-signature` on spaces, keep the tokens prefixed\n   `v1a,`, and base64-decode the remainder of each into a 64-byte\n   signature.\n4. Verify against the keys published at\n   `https://api.confirmo.com/.well-known/jwks.json`\n   (`kty: OKP`, `crv: Ed25519`, `alg: EdDSA`, `use: sig`). Accept the\n   request if any key verifies any candidate signature. Cache the\n   document for at most 5 minutes: signing keys rotate — at least\n   annually, and on demand — with a 30-day window during which both the\n   old and the new key are published, and the request carries no key id.\n\n**Absent vs. null.** A field of `data` with no value is omitted; we never\nsend an explicit `null`. The nullable fields in `Subscription` and\n`Payment` mark values that may be absent, not values delivered as `null`,\nso you only have to handle the absent case. New fields and new event\ntypes may be added without a version bump, so ignore what you don't\nrecognize.\n"
    }
  ],
  "paths": {
    "/api/v3/subscriptions/plans": {
      "post": {
        "operationId": "createPlan",
        "tags": [
          "Plans"
        ],
        "summary": "Create a subscription plan",
        "description": "Create a plan your subscribers can subscribe to.\n",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreatePlanRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Plan created.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Plan"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "422": {
            "$ref": "#/components/responses/UnprocessableEntity"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        }
      },
      "get": {
        "operationId": "listPlans",
        "tags": [
          "Plans"
        ],
        "summary": "List your subscription plans",
        "description": "Returns all your plans, newest first, regardless of status. Use the\n`nextCursor` from the previous response to fetch the next page.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/PageSize"
          },
          {
            "$ref": "#/components/parameters/PageCursor"
          }
        ],
        "responses": {
          "200": {
            "description": "Your plans, newest first.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PlanPage"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        }
      }
    },
    "/api/v3/subscriptions/plans/{planId}": {
      "get": {
        "operationId": "getPlan",
        "tags": [
          "Plans"
        ],
        "summary": "Get a subscription plan by ID",
        "parameters": [
          {
            "$ref": "#/components/parameters/PlanIdPath"
          }
        ],
        "responses": {
          "200": {
            "description": "Plan found.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Plan"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        }
      }
    },
    "/api/v3/subscriptions/plans/{planId}/archive": {
      "post": {
        "operationId": "archivePlan",
        "tags": [
          "Plans"
        ],
        "summary": "Archive a subscription plan",
        "description": "Archived plans can't be subscribed to, but existing subscriptions\non the plan continue billing unchanged. Archiving is one-way and\nidempotent.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/PlanIdPath"
          }
        ],
        "responses": {
          "200": {
            "description": "Plan archived.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Plan"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        }
      }
    },
    "/api/v3/subscriptions": {
      "post": {
        "operationId": "createSubscription",
        "tags": [
          "Subscriptions"
        ],
        "summary": "Create a subscription",
        "description": "Subscribes a subscriber (by email) to one of your plans. Starts in\n`PENDING` until the subscriber completes checkout. Pass\n`customerProfile` to supply Travel Rule / AML customer data.\n",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateSubscriptionRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Subscription created.",
            "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": {
            "$ref": "#/components/responses/Conflict"
          },
          "422": {
            "$ref": "#/components/responses/UnprocessableEntity"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        }
      },
      "get": {
        "operationId": "listSubscriptions",
        "tags": [
          "Subscriptions"
        ],
        "summary": "List your subscriptions",
        "description": "Returns your subscriptions, newest first. Use the filters to\nnarrow by status, `customerProfileId`, or createdAt window.\n",
        "parameters": [
          {
            "name": "status",
            "in": "query",
            "required": false,
            "description": "Repeatable. Limits the result to subscriptions in any of the given statuses.",
            "schema": {
              "type": "array",
              "items": {
                "$ref": "#/components/schemas/SubscriptionStatus"
              }
            },
            "style": "form",
            "explode": true
          },
          {
            "name": "customerProfileId",
            "in": "query",
            "required": false,
            "description": "Look up a subscription by the `customerProfileId` you supplied when creating it.",
            "schema": {
              "type": "string",
              "maxLength": 128
            }
          },
          {
            "name": "createdFrom",
            "in": "query",
            "required": false,
            "description": "Inclusive lower bound on `createdAt`, Unix epoch seconds.",
            "schema": {
              "type": "integer",
              "format": "int64"
            }
          },
          {
            "name": "createdTo",
            "in": "query",
            "required": false,
            "description": "Exclusive upper bound on `createdAt`, Unix epoch seconds.",
            "schema": {
              "type": "integer",
              "format": "int64"
            }
          },
          {
            "$ref": "#/components/parameters/PageSize"
          },
          {
            "$ref": "#/components/parameters/PageCursor"
          }
        ],
        "responses": {
          "200": {
            "description": "Subscriptions matching the filter.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SubscriptionPage"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        }
      }
    },
    "/api/v3/subscriptions/{subscriptionId}": {
      "get": {
        "operationId": "getSubscription",
        "tags": [
          "Subscriptions"
        ],
        "summary": "Get a subscription by ID",
        "parameters": [
          {
            "$ref": "#/components/parameters/SubscriptionIdPath"
          }
        ],
        "responses": {
          "200": {
            "description": "Subscription found.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Subscription"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        }
      }
    },
    "/api/v3/subscriptions/{subscriptionId}/cancel": {
      "post": {
        "operationId": "cancelSubscription",
        "tags": [
          "Subscriptions"
        ],
        "summary": "Cancel a subscription",
        "description": "Stops future billing. Idempotent — canceling an already-canceled\nsubscription returns its current state. A subscription that has\nalready expired cannot be canceled (`409`).\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/SubscriptionIdPath"
          }
        ],
        "responses": {
          "200": {
            "description": "Subscription canceled.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Subscription"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        }
      }
    },
    "/api/v3/subscriptions/payments": {
      "get": {
        "operationId": "listPayments",
        "tags": [
          "Payments"
        ],
        "summary": "List payments",
        "description": "Returns payments across all your subscriptions, newest first. Use\nthe filters to scope to a single subscription, restrict by status,\nor restrict to a createdAt window.\n",
        "parameters": [
          {
            "name": "subscriptionId",
            "in": "query",
            "required": false,
            "description": "Restrict to one subscription.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "status",
            "in": "query",
            "required": false,
            "description": "Repeatable. Limits the result to payments in any of the given statuses.",
            "schema": {
              "type": "array",
              "items": {
                "$ref": "#/components/schemas/PaymentStatus"
              }
            },
            "style": "form",
            "explode": true
          },
          {
            "name": "createdFrom",
            "in": "query",
            "required": false,
            "description": "Inclusive lower bound on `createdAt`, Unix epoch seconds.",
            "schema": {
              "type": "integer",
              "format": "int64"
            }
          },
          {
            "name": "createdTo",
            "in": "query",
            "required": false,
            "description": "Exclusive upper bound on `createdAt`, Unix epoch seconds.",
            "schema": {
              "type": "integer",
              "format": "int64"
            }
          },
          {
            "$ref": "#/components/parameters/PageSize"
          },
          {
            "$ref": "#/components/parameters/PageCursor"
          }
        ],
        "responses": {
          "200": {
            "description": "Payments matching the filter.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PaymentPage"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        }
      }
    },
    "/api/v3/subscriptions/payments/{paymentId}": {
      "get": {
        "operationId": "getPayment",
        "tags": [
          "Payments"
        ],
        "summary": "Get a payment by ID",
        "parameters": [
          {
            "name": "paymentId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Payment found.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Payment"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        }
      }
    }
  },
  "webhooks": {
    "subscription.created": {
      "post": {
        "operationId": "subscriptionCreatedEvent",
        "tags": [
          "Webhooks"
        ],
        "summary": "A subscription was created",
        "description": "Sent when a subscription is created. It usually starts in `PENDING`\nwhile the subscriber completes checkout — send them to\n`data.checkoutUrl`. `data` is the full `Subscription`.\n",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SubscriptionCreatedEvent"
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Return any 2xx status to acknowledge receipt. Any other response\n(or a timeout) is treated as a delivery failure and retried.\n"
          }
        }
      }
    },
    "subscription.activated": {
      "post": {
        "operationId": "subscriptionActivatedEvent",
        "tags": [
          "Webhooks"
        ],
        "summary": "A subscription became active",
        "description": "Sent when a subscription becomes `ACTIVE` — the subscriber has\ncompleted checkout and billing is now on schedule. `data` is the full\n`Subscription`.\n",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SubscriptionActivatedEvent"
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Return any 2xx status to acknowledge receipt. Any other response\n(or a timeout) is treated as a delivery failure and retried.\n"
          }
        }
      }
    },
    "subscription.past_due": {
      "post": {
        "operationId": "subscriptionPastDueEvent",
        "tags": [
          "Webhooks"
        ],
        "summary": "A subscription became past due",
        "description": "Sent when a charge fails and the subscription enters `PAST_DUE`.\nBilling retries during the grace period before a terminal state.\n`data` is the full `Subscription`.\n",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SubscriptionPastDueEvent"
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Return any 2xx status to acknowledge receipt. Any other response\n(or a timeout) is treated as a delivery failure and retried.\n"
          }
        }
      }
    },
    "subscription.resumed": {
      "post": {
        "operationId": "subscriptionResumedEvent",
        "tags": [
          "Webhooks"
        ],
        "summary": "A past-due subscription recovered",
        "description": "Sent when a `PAST_DUE` subscription recovers — a retry succeeded\nwithin the grace period and billing is `ACTIVE` again. `data` is the\nfull `Subscription`.\n",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SubscriptionResumedEvent"
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Return any 2xx status to acknowledge receipt. Any other response\n(or a timeout) is treated as a delivery failure and retried.\n"
          }
        }
      }
    },
    "subscription.canceled": {
      "post": {
        "operationId": "subscriptionCanceledEvent",
        "tags": [
          "Webhooks"
        ],
        "summary": "A subscription was canceled",
        "description": "Sent when a subscription is canceled before its natural end. See\n`data.cancellation.reason` for who canceled it and why. `data` is the\nfull `Subscription`.\n",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SubscriptionCanceledEvent"
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Return any 2xx status to acknowledge receipt. Any other response\n(or a timeout) is treated as a delivery failure and retried.\n"
          }
        }
      }
    },
    "subscription.expired": {
      "post": {
        "operationId": "subscriptionExpiredEvent",
        "tags": [
          "Webhooks"
        ],
        "summary": "A subscription expired",
        "description": "Sent when a subscription ends naturally — its fixed term completed, or\npayment retries were exhausted. See `data.expiration.reason`. `data`\nis the full `Subscription`.\n",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SubscriptionExpiredEvent"
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Return any 2xx status to acknowledge receipt. Any other response\n(or a timeout) is treated as a delivery failure and retried.\n"
          }
        }
      }
    },
    "subscription.payment.succeeded": {
      "post": {
        "operationId": "subscriptionPaymentSucceededEvent",
        "tags": [
          "Webhooks"
        ],
        "summary": "A recurring charge succeeded",
        "description": "Sent when a recurring charge settles successfully. `data` is the\n`Payment` record; `resourceId` and `data.subscriptionId` are the\nparent subscription.\n",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SubscriptionPaymentSucceededEvent"
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Return any 2xx status to acknowledge receipt. Any other response\n(or a timeout) is treated as a delivery failure and retried.\n"
          }
        }
      }
    },
    "subscription.payment.failed": {
      "post": {
        "operationId": "subscriptionPaymentFailedEvent",
        "tags": [
          "Webhooks"
        ],
        "summary": "A recurring charge failed",
        "description": "Sent when a recurring charge fails to settle. `data` is the `Payment`\nrecord; see `data.failure.reason`. `resourceId` and\n`data.subscriptionId` are the parent subscription. A failed charge is\nfollowed by `subscription.past_due` once the subscription enters the\ngrace period.\n",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SubscriptionPaymentFailedEvent"
              }
            }
          }
        },
        "responses": {
          "2XX": {
            "description": "Return any 2xx status to acknowledge receipt. Any other response\n(or a timeout) is treated as a delivery failure and retried.\n"
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "JWT",
        "description": "Bearer API key issued from your Confirmo dashboard. The key\nidentifies your account — you don't supply a merchant ID on\nindividual requests.\n"
      }
    },
    "parameters": {
      "PageSize": {
        "name": "pageSize",
        "in": "query",
        "required": false,
        "description": "Maximum items per page. Defaults to 20, capped at 100.",
        "schema": {
          "type": "integer",
          "minimum": 1,
          "maximum": 100,
          "default": 20
        }
      },
      "PlanIdPath": {
        "name": "planId",
        "in": "path",
        "required": true,
        "schema": {
          "type": "string",
          "format": "uuid"
        }
      },
      "SubscriptionIdPath": {
        "name": "subscriptionId",
        "in": "path",
        "required": true,
        "schema": {
          "type": "string",
          "format": "uuid"
        }
      },
      "PageCursor": {
        "name": "cursor",
        "in": "query",
        "required": false,
        "schema": {
          "type": "string"
        }
      }
    },
    "schemas": {
      "CreatePlanRequest": {
        "type": "object",
        "description": "A `FIXED` plan carries a `price` (every subscription bills it) and must omit\n`asset`. A `VARIABLE` plan carries only an `asset` — the amount is supplied\nper subscription at creation — and must omit `price`.\n",
        "required": [
          "name",
          "type",
          "billingInterval",
          "gracePeriod"
        ],
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 256
          },
          "type": {
            "$ref": "#/components/schemas/PlanType"
          },
          "price": {
            "$ref": "#/components/schemas/Price",
            "description": "Required for `FIXED` plans; must be omitted for `VARIABLE` plans."
          },
          "asset": {
            "type": "string",
            "pattern": "^[A-Z0-9]{3,16}$",
            "example": "USD",
            "description": "Billing asset for a `VARIABLE` plan (the amount is supplied per\nsubscription). Required when `type` is `VARIABLE`; ignored for `FIXED`\n(asset is taken from `price.asset`).\n"
          },
          "billingInterval": {
            "$ref": "#/components/schemas/BillingInterval"
          },
          "termCycles": {
            "type": "integer",
            "minimum": 1,
            "nullable": true,
            "description": "Number of billing cycles before the subscription term ends. Unit follows `billingInterval`. Null or omitted means the subscription renews indefinitely.\n"
          },
          "gracePeriod": {
            "$ref": "#/components/schemas/TimePeriod",
            "description": "How long failed payments are retried while the subscription is `PAST_DUE` before it reaches a terminal state. Must be a whole number of days between 1 and 7 (`P1D`–`P7D`).\n"
          }
        }
      },
      "Plan": {
        "type": "object",
        "required": [
          "id",
          "name",
          "type",
          "asset",
          "billingInterval",
          "gracePeriod",
          "status",
          "createdAt",
          "updatedAt"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "type": {
            "$ref": "#/components/schemas/PlanType"
          },
          "asset": {
            "type": "string",
            "pattern": "^[A-Z0-9]{3,16}$",
            "example": "USD",
            "description": "The plan's billing asset. Always present (equals `price.asset` for `FIXED` plans)."
          },
          "price": {
            "$ref": "#/components/schemas/Price",
            "description": "The fixed price for `FIXED` plans; absent for `VARIABLE` plans."
          },
          "billingInterval": {
            "$ref": "#/components/schemas/BillingInterval"
          },
          "termCycles": {
            "type": "integer",
            "minimum": 1,
            "nullable": true,
            "description": "Number of billing cycles in the plan's term; null means it renews indefinitely."
          },
          "gracePeriod": {
            "$ref": "#/components/schemas/TimePeriod",
            "description": "How long failed payments are retried while the subscription is `PAST_DUE` before it reaches a terminal state. Always a whole number of days between 1 and 7 (`P1D`–`P7D`).\n"
          },
          "status": {
            "$ref": "#/components/schemas/PlanStatus"
          },
          "createdAt": {
            "$ref": "#/components/schemas/EpochSeconds"
          },
          "updatedAt": {
            "$ref": "#/components/schemas/EpochSeconds"
          }
        }
      },
      "PlanPage": {
        "type": "object",
        "required": [
          "items",
          "hasMore"
        ],
        "properties": {
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Plan"
            }
          },
          "nextCursor": {
            "type": "string",
            "description": "Opaque cursor for the next page. Present only when `hasMore` is true."
          },
          "hasMore": {
            "type": "boolean"
          }
        }
      },
      "Price": {
        "type": "object",
        "description": "Monetary amount. `amount` is a decimal string. `asset` is an\nISO 4217 code for fiat or an uppercase stablecoin symbol —\ne.g. `USD`, `EUR`, `USDC`, `USDT`.\n",
        "required": [
          "amount",
          "asset"
        ],
        "properties": {
          "amount": {
            "type": "string",
            "pattern": "^-?\\d+(\\.\\d+)?$",
            "example": "29.00"
          },
          "asset": {
            "type": "string",
            "pattern": "^[A-Z0-9]{3,16}$",
            "example": "USD"
          }
        }
      },
      "BillingInterval": {
        "type": "string",
        "description": "How often the subscription is billed.",
        "enum": [
          "MONTHLY",
          "ANNUAL"
        ]
      },
      "EpochSeconds": {
        "type": "integer",
        "format": "int64",
        "description": "Unix epoch seconds.",
        "example": 1748513400
      },
      "CurrentPeriod": {
        "type": "object",
        "description": "The current billing period. Null before the subscription is `ACTIVE`.",
        "required": [
          "start",
          "end"
        ],
        "properties": {
          "start": {
            "$ref": "#/components/schemas/EpochSeconds",
            "description": "Start of the current billing period."
          },
          "end": {
            "$ref": "#/components/schemas/EpochSeconds",
            "description": "End of the current billing period."
          }
        }
      },
      "Cancellation": {
        "type": "object",
        "description": "Cancellation details. Null unless `status` is `CANCELED`.",
        "required": [
          "at",
          "reason"
        ],
        "properties": {
          "at": {
            "$ref": "#/components/schemas/EpochSeconds",
            "description": "When the subscription was canceled."
          },
          "reason": {
            "$ref": "#/components/schemas/CancelReason",
            "description": "Why the subscription was canceled."
          }
        }
      },
      "Expiration": {
        "type": "object",
        "description": "Expiration details. Null unless `status` is `EXPIRED`.",
        "required": [
          "at",
          "reason"
        ],
        "properties": {
          "at": {
            "$ref": "#/components/schemas/EpochSeconds",
            "description": "When the subscription expired."
          },
          "reason": {
            "$ref": "#/components/schemas/ExpiryReason",
            "description": "Why the subscription expired."
          }
        }
      },
      "PaymentFailure": {
        "type": "object",
        "description": "Failure details. Null unless `status` is `FAILED`.",
        "required": [
          "reason"
        ],
        "properties": {
          "reason": {
            "type": "string",
            "description": "Classification of the failure. One of a fixed set of values: `INSUFFICIENT_BALANCE`, `INSUFFICIENT_ALLOWANCE`, `ON_CHAIN_REVERT`, `DROPPED_BY_BLOCKCHAIN`, `REJECTED_BY_COMPLIANCE`, `PROVIDER_ERROR`, `UNKNOWN`."
          }
        }
      },
      "PlanStatus": {
        "type": "string",
        "description": "`ACTIVE` — open for new subscriptions.\n`ARCHIVED` — closed to new subscriptions; existing subscriptions keep billing.\n",
        "enum": [
          "ACTIVE",
          "ARCHIVED"
        ]
      },
      "PlanType": {
        "type": "string",
        "description": "How a plan's billed amount is determined.\n`FIXED` — the plan carries a fixed `price`; every subscription bills it.\n`VARIABLE` — the plan carries only an `asset`; the amount is set per\nsubscription at creation.\n",
        "enum": [
          "FIXED",
          "VARIABLE"
        ]
      },
      "TimePeriod": {
        "type": "string",
        "description": "ISO-8601 calendar period. Examples: `P7D` (7 days), `P1M`\n(1 month), `P1Y` (1 year). Sub-day precision is not supported.\n",
        "pattern": "^P(\\d+Y)?(\\d+M)?(\\d+W)?(\\d+D)?$",
        "example": "P3D"
      },
      "CreateSubscriptionRequest": {
        "type": "object",
        "required": [
          "planId",
          "subscriber"
        ],
        "properties": {
          "planId": {
            "type": "string",
            "format": "uuid"
          },
          "subscriber": {
            "$ref": "#/components/schemas/SubscriberRef"
          },
          "customerProfile": {
            "$ref": "#/components/schemas/CustomerProfile"
          },
          "amount": {
            "type": "string",
            "pattern": "^\\d+(\\.\\d+)?$",
            "example": "49.00",
            "description": "Per-subscription amount (value only; asset comes from the plan).\nRequired when the plan is `VARIABLE`; must be omitted when the plan is `FIXED`.\n"
          },
          "notifyUrl": {
            "type": "string",
            "format": "uri",
            "description": "Webhook URL (http/https, max 2048 chars) for lifecycle and payment events."
          },
          "returnUrl": {
            "type": "string",
            "format": "uri",
            "description": "Where the subscriber is redirected after completing or exiting checkout (http/https, max 2048 chars)."
          }
        }
      },
      "SubscriberRef": {
        "type": "object",
        "required": [
          "email"
        ],
        "properties": {
          "email": {
            "type": "string",
            "format": "email",
            "maxLength": 254,
            "description": "Subscriber's email."
          }
        }
      },
      "Subscription": {
        "type": "object",
        "description": "Lifecycle: `PENDING` → (optional `TRIALING` before the first charge) →\n`ACTIVE` after checkout → `PAST_DUE` on a failed charge → terminal\n`CANCELED` / `EXPIRED`.\n",
        "required": [
          "id",
          "planId",
          "planName",
          "status",
          "cycleNumber",
          "amount",
          "createdAt",
          "updatedAt"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "planId": {
            "type": "string",
            "format": "uuid"
          },
          "planName": {
            "type": "string",
            "description": "Current name of the plan."
          },
          "amount": {
            "$ref": "#/components/schemas/Price",
            "description": "Resolved per-cycle charge in the plan's asset — the plan `price` for\n`FIXED` plans, or this subscription's amount for `VARIABLE` plans.\n"
          },
          "customerProfileId": {
            "type": "string",
            "nullable": true,
            "description": "Your customer profile id, as supplied at creation (customerProfile.profileId)."
          },
          "email": {
            "type": "string",
            "format": "email",
            "nullable": true,
            "description": "Subscriber's email address. Null when the subscriber has no email on file."
          },
          "notifyUrl": {
            "type": "string",
            "format": "uri",
            "nullable": true,
            "description": "Webhook URL for lifecycle and payment events, as supplied at creation."
          },
          "returnUrl": {
            "type": "string",
            "format": "uri",
            "nullable": true,
            "description": "Where the subscriber is redirected after checkout, as supplied at creation."
          },
          "checkoutUrl": {
            "type": "string",
            "format": "uri",
            "nullable": true,
            "description": "Hosted checkout link to send the subscriber to. Present while the subscription is awaiting checkout (`PENDING`); null once checkout is complete or no longer applicable.\n"
          },
          "status": {
            "$ref": "#/components/schemas/SubscriptionStatus"
          },
          "startDate": {
            "$ref": "#/components/schemas/EpochSeconds",
            "nullable": true,
            "description": "When the subscription started its first cycle. Null until it becomes `ACTIVE`."
          },
          "cycleNumber": {
            "type": "integer",
            "minimum": 0,
            "description": "How many billing cycles have started."
          },
          "currentPeriod": {
            "allOf": [
              {
                "$ref": "#/components/schemas/CurrentPeriod"
              }
            ],
            "nullable": true,
            "description": "Current billing period. Null before the subscription is `ACTIVE`."
          },
          "nextPaymentDate": {
            "$ref": "#/components/schemas/EpochSeconds",
            "nullable": true,
            "description": "When the next charge is due. Null when no further charge is scheduled."
          },
          "endsAt": {
            "$ref": "#/components/schemas/EpochSeconds",
            "nullable": true,
            "description": "When the subscription's fixed term ends, for plans with a finite `termCycles`. Null for subscriptions that renew indefinitely.\n"
          },
          "cancellation": {
            "allOf": [
              {
                "$ref": "#/components/schemas/Cancellation"
              }
            ],
            "nullable": true,
            "description": "Cancellation details. Null unless `status` is `CANCELED`."
          },
          "expiration": {
            "allOf": [
              {
                "$ref": "#/components/schemas/Expiration"
              }
            ],
            "nullable": true,
            "description": "Expiration details. Null unless `status` is `EXPIRED`."
          },
          "createdAt": {
            "$ref": "#/components/schemas/EpochSeconds"
          },
          "updatedAt": {
            "$ref": "#/components/schemas/EpochSeconds"
          }
        }
      },
      "SubscriptionPage": {
        "type": "object",
        "required": [
          "items",
          "hasMore"
        ],
        "properties": {
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Subscription"
            }
          },
          "nextCursor": {
            "type": "string",
            "description": "Opaque cursor for the next page. Present only when `hasMore` is true."
          },
          "hasMore": {
            "type": "boolean"
          }
        }
      },
      "SubscriptionStatus": {
        "type": "string",
        "description": "Subscription lifecycle state.\n`PENDING` — created; awaiting subscriber checkout.\n`TRIALING` — in a trial period before the first charge.\n`ACTIVE` — checkout complete; billing on schedule.\n`PAST_DUE` — a charge failed; retrying within the grace period before a terminal state.\n`CANCELED` — stopped before its natural end (see `cancelReason`).\n`EXPIRED` — ended naturally or after retries were exhausted (see `expiryReason`).\n",
        "enum": [
          "PENDING",
          "TRIALING",
          "ACTIVE",
          "PAST_DUE",
          "CANCELED",
          "EXPIRED"
        ]
      },
      "CancelReason": {
        "type": "string",
        "description": "Why a subscription was canceled.\n`SUBSCRIBER` — canceled by the subscriber.\n`MERCHANT` — canceled by you.\n`MERCHANT_DISABLED` — canceled because your account was disabled.\n`ADMIN` — canceled by Confirmo (e.g. for compliance reasons).\n`EXPIRED` — canceled as part of the subscription expiring.\n`DUNNING_EXHAUSTED` — automatically canceled after payment retries were exhausted.\n`SANCTIONS_HIT` — canceled automatically because a sanctions/risk screening blocked the payment.\n`CHECKOUT_TIMEOUT` — canceled automatically because the checkout was not completed before it timed out.\n",
        "enum": [
          "SUBSCRIBER",
          "MERCHANT",
          "MERCHANT_DISABLED",
          "ADMIN",
          "EXPIRED",
          "DUNNING_EXHAUSTED",
          "SANCTIONS_HIT",
          "CHECKOUT_TIMEOUT"
        ]
      },
      "ExpiryReason": {
        "type": "string",
        "description": "Why a subscription expired.\n`TERM_REACHED` — the plan's fixed `termCycles` completed.\n`PAYMENT_FAILED` — automatically ended after payment retries were exhausted.\n",
        "enum": [
          "TERM_REACHED",
          "PAYMENT_FAILED"
        ]
      },
      "Payment": {
        "type": "object",
        "description": "A single recurring-billing charge. One record per attempted cycle;\nstarts as `PENDING` and settles to `SUCCEEDED` or `FAILED`.\n",
        "required": [
          "id",
          "subscriptionId",
          "billedAmount",
          "paidAmount",
          "status",
          "cycleNumber",
          "createdAt"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "subscriptionId": {
            "type": "string",
            "format": "uuid"
          },
          "billedAmount": {
            "$ref": "#/components/schemas/Price",
            "description": "Amount billed, in the subscription's billing asset."
          },
          "paidAmount": {
            "$ref": "#/components/schemas/Price",
            "description": "Amount settled on chain, in the asset paid."
          },
          "balance": {
            "allOf": [
              {
                "$ref": "#/components/schemas/PaymentBalance"
              }
            ],
            "nullable": true,
            "description": "What the merchant is credited in, with the fee broken down in that asset. Null for non-SUCCEEDED payments."
          },
          "status": {
            "$ref": "#/components/schemas/PaymentStatus"
          },
          "paidAt": {
            "$ref": "#/components/schemas/EpochSeconds",
            "nullable": true,
            "description": "When the payment settled. Null until `status` is `SUCCEEDED`."
          },
          "failure": {
            "allOf": [
              {
                "$ref": "#/components/schemas/PaymentFailure"
              }
            ],
            "nullable": true,
            "description": "Failure details. Null unless `status` is `FAILED`."
          },
          "cycleNumber": {
            "type": "integer",
            "minimum": 0,
            "description": "Which billing cycle this payment belongs to."
          },
          "createdAt": {
            "$ref": "#/components/schemas/EpochSeconds"
          }
        }
      },
      "PaymentPage": {
        "type": "object",
        "required": [
          "items",
          "hasMore"
        ],
        "properties": {
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Payment"
            }
          },
          "nextCursor": {
            "type": "string",
            "description": "Opaque cursor for the next page. Present only when `hasMore` is true."
          },
          "hasMore": {
            "type": "boolean"
          }
        }
      },
      "PaymentStatus": {
        "type": "string",
        "description": "`PENDING` — charge initiated, not yet settled.\n`SUCCEEDED` — charge settled successfully.\n`FAILED` — charge did not settle.\n",
        "enum": [
          "PENDING",
          "SUCCEEDED",
          "FAILED"
        ]
      },
      "PaymentBalance": {
        "type": "object",
        "required": [
          "asset",
          "amount",
          "feeBase",
          "feeProvider",
          "feeNetwork",
          "feeTotal"
        ],
        "description": "Balance view of a succeeded payment. `asset` is the fiat code for a\nconversion, or the paid stablecoin for \"same crypto as paid\".\n`feeTotal == feeBase + feeProvider + feeNetwork`.\n",
        "properties": {
          "asset": {
            "type": "string",
            "description": "AnyAsset code (fiat or stablecoin)."
          },
          "amount": {
            "type": "string",
            "description": "Gross credited, in `asset`."
          },
          "feeBase": {
            "type": "string"
          },
          "feeProvider": {
            "type": "string"
          },
          "feeNetwork": {
            "type": "string"
          },
          "feeTotal": {
            "type": "string"
          }
        }
      },
      "WebhookEnvelope": {
        "type": "object",
        "description": "The body POSTed to your `notifyUrl` for every webhook event. `data`\ncarries the affected resource — a `Subscription` for lifecycle events,\na `Payment` for payment events.\n",
        "required": [
          "id",
          "type",
          "timestamp",
          "sequence",
          "resourceType",
          "resourceId",
          "data"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "Unique id of this delivery (UUIDv7), mirrored in the `webhook-id`\nheader and covered by the signature. Retries of the same event\nrepeat this id, so use it to dedupe. A replayed event gets a new\nid — see `sequence`.\n"
          },
          "type": {
            "type": "string",
            "description": "Identifies the event — one of the types listed under Webhooks.\nAlso sent as the `webhook-type` header.\n",
            "example": "subscription.activated"
          },
          "timestamp": {
            "type": "string",
            "format": "date-time",
            "description": "When the event occurred (ISO-8601 UTC, sub-second precision). This\nis the event's own time and is preserved on retries and replays —\nit is not the `webhook-timestamp` header, which is stamped per\nattempt and is what the signature covers.\n",
            "example": "2026-05-29T08:04:12.512834071Z"
          },
          "sequence": {
            "type": "integer",
            "format": "int64",
            "minimum": 1,
            "description": "Monotonically increasing counter, per `resourceId`, starting at 1.\nPayment events share their parent subscription's counter. Apply an\nevent only if its `sequence` is higher than the last one you\napplied for that `resourceId`; that discards both stale\nredeliveries and replays of old events. Also sent as the\n`webhook-sequence` header. Not guaranteed gap-free.\n"
          },
          "resourceType": {
            "type": "string",
            "const": "subscription",
            "description": "The kind of resource this event is about. Always `subscription`."
          },
          "resourceId": {
            "type": "string",
            "format": "uuid",
            "description": "The subscription this event concerns — always the subscription id,\neven for payment events, which route to their parent subscription.\n"
          },
          "data": {
            "type": "object",
            "description": "The affected resource, typed per event: a `Subscription` for the\nlifecycle events, a `Payment` for the payment events. See the\nindividual events below for the exact type and an example.\n"
          }
        }
      },
      "SubscriptionCreatedEvent": {
        "description": "A `subscription.created` event.",
        "allOf": [
          {
            "$ref": "#/components/schemas/WebhookEnvelope"
          },
          {
            "type": "object",
            "required": [
              "data"
            ],
            "properties": {
              "type": {
                "const": "subscription.created"
              },
              "data": {
                "$ref": "#/components/schemas/Subscription"
              }
            }
          }
        ],
        "example": {
          "id": "019807c2-6f30-7c5a-b0e4-3d81f2a6c910",
          "type": "subscription.created",
          "timestamp": "2026-05-29T07:58:41.204118773Z",
          "sequence": 1,
          "resourceType": "subscription",
          "resourceId": "019807c2-6f2a-7b41-9c58-2f9c1a4b8d21",
          "data": {
            "id": "019807c2-6f2a-7b41-9c58-2f9c1a4b8d21",
            "planId": "019801a3-4d5e-7f80-9a1b-2c3d4e5f6071",
            "planName": "Pro Monthly",
            "amount": {
              "amount": "29.00",
              "asset": "USD"
            },
            "customerProfileId": "acct_8842",
            "email": "ada@example.com",
            "notifyUrl": "https://merchant.example/confirmo/webhook",
            "returnUrl": "https://merchant.example/subscription/thanks",
            "checkoutUrl": "https://subscribe.confirmo.com/checkout/019807c2-6f2a-7b41-9c58-2f9c1a4b8d21",
            "status": "PENDING",
            "cycleNumber": 0,
            "createdAt": 1780041521,
            "updatedAt": 1780041521
          }
        }
      },
      "SubscriptionActivatedEvent": {
        "description": "A `subscription.activated` event.",
        "allOf": [
          {
            "$ref": "#/components/schemas/WebhookEnvelope"
          },
          {
            "type": "object",
            "required": [
              "data"
            ],
            "properties": {
              "type": {
                "const": "subscription.activated"
              },
              "data": {
                "$ref": "#/components/schemas/Subscription"
              }
            }
          }
        ],
        "example": {
          "id": "019807c3-1a04-7de2-84b7-9c0f5a2e6188",
          "type": "subscription.activated",
          "timestamp": "2026-05-29T08:04:12.512834071Z",
          "sequence": 2,
          "resourceType": "subscription",
          "resourceId": "019807c2-6f2a-7b41-9c58-2f9c1a4b8d21",
          "data": {
            "id": "019807c2-6f2a-7b41-9c58-2f9c1a4b8d21",
            "planId": "019801a3-4d5e-7f80-9a1b-2c3d4e5f6071",
            "planName": "Pro Monthly",
            "amount": {
              "amount": "29.00",
              "asset": "USD"
            },
            "customerProfileId": "acct_8842",
            "email": "ada@example.com",
            "notifyUrl": "https://merchant.example/confirmo/webhook",
            "returnUrl": "https://merchant.example/subscription/thanks",
            "status": "ACTIVE",
            "startDate": 1780041852,
            "cycleNumber": 1,
            "currentPeriod": {
              "start": 1780041852,
              "end": 1782720252
            },
            "nextPaymentDate": 1782720252,
            "createdAt": 1780041521,
            "updatedAt": 1780041852
          }
        }
      },
      "SubscriptionPastDueEvent": {
        "description": "A `subscription.past_due` event.",
        "allOf": [
          {
            "$ref": "#/components/schemas/WebhookEnvelope"
          },
          {
            "type": "object",
            "required": [
              "data"
            ],
            "properties": {
              "type": {
                "const": "subscription.past_due"
              },
              "data": {
                "$ref": "#/components/schemas/Subscription"
              }
            }
          }
        ],
        "example": {
          "id": "01981e37-9b8f-7a41-b2ce-70d418c5f302",
          "type": "subscription.past_due",
          "timestamp": "2026-07-29T08:00:09.663201480Z",
          "sequence": 5,
          "resourceType": "subscription",
          "resourceId": "019807c2-6f2a-7b41-9c58-2f9c1a4b8d21",
          "data": {
            "id": "019807c2-6f2a-7b41-9c58-2f9c1a4b8d21",
            "planId": "019801a3-4d5e-7f80-9a1b-2c3d4e5f6071",
            "planName": "Pro Monthly",
            "amount": {
              "amount": "29.00",
              "asset": "USD"
            },
            "customerProfileId": "acct_8842",
            "email": "ada@example.com",
            "notifyUrl": "https://merchant.example/confirmo/webhook",
            "returnUrl": "https://merchant.example/subscription/thanks",
            "status": "PAST_DUE",
            "startDate": 1780041852,
            "cycleNumber": 3,
            "currentPeriod": {
              "start": 1785312252,
              "end": 1787990652
            },
            "nextPaymentDate": 1785398652,
            "createdAt": 1780041521,
            "updatedAt": 1785312009
          }
        }
      },
      "SubscriptionResumedEvent": {
        "description": "A `subscription.resumed` event.",
        "allOf": [
          {
            "$ref": "#/components/schemas/WebhookEnvelope"
          },
          {
            "type": "object",
            "required": [
              "data"
            ],
            "properties": {
              "type": {
                "const": "subscription.resumed"
              },
              "data": {
                "$ref": "#/components/schemas/Subscription"
              }
            }
          }
        ],
        "example": {
          "id": "019822a5-04c1-7b0e-9d33-51ae7f2c8b64",
          "type": "subscription.resumed",
          "timestamp": "2026-07-30T09:15:00.881044219Z",
          "sequence": 7,
          "resourceType": "subscription",
          "resourceId": "019807c2-6f2a-7b41-9c58-2f9c1a4b8d21",
          "data": {
            "id": "019807c2-6f2a-7b41-9c58-2f9c1a4b8d21",
            "planId": "019801a3-4d5e-7f80-9a1b-2c3d4e5f6071",
            "planName": "Pro Monthly",
            "amount": {
              "amount": "29.00",
              "asset": "USD"
            },
            "customerProfileId": "acct_8842",
            "email": "ada@example.com",
            "notifyUrl": "https://merchant.example/confirmo/webhook",
            "returnUrl": "https://merchant.example/subscription/thanks",
            "status": "ACTIVE",
            "startDate": 1780041852,
            "cycleNumber": 3,
            "currentPeriod": {
              "start": 1785312252,
              "end": 1787990652
            },
            "nextPaymentDate": 1787990652,
            "createdAt": 1780041521,
            "updatedAt": 1785402900
          }
        }
      },
      "SubscriptionCanceledEvent": {
        "description": "A `subscription.canceled` event.",
        "allOf": [
          {
            "$ref": "#/components/schemas/WebhookEnvelope"
          },
          {
            "type": "object",
            "required": [
              "data"
            ],
            "properties": {
              "type": {
                "const": "subscription.canceled"
              },
              "data": {
                "$ref": "#/components/schemas/Subscription"
              }
            }
          }
        ],
        "example": {
          "id": "019832f1-7ac6-7d58-8f19-2b6e04d1a9c7",
          "type": "subscription.canceled",
          "timestamp": "2026-08-02T11:20:00.019873542Z",
          "sequence": 8,
          "resourceType": "subscription",
          "resourceId": "019807c2-6f2a-7b41-9c58-2f9c1a4b8d21",
          "data": {
            "id": "019807c2-6f2a-7b41-9c58-2f9c1a4b8d21",
            "planId": "019801a3-4d5e-7f80-9a1b-2c3d4e5f6071",
            "planName": "Pro Monthly",
            "amount": {
              "amount": "29.00",
              "asset": "USD"
            },
            "customerProfileId": "acct_8842",
            "email": "ada@example.com",
            "notifyUrl": "https://merchant.example/confirmo/webhook",
            "returnUrl": "https://merchant.example/subscription/thanks",
            "status": "CANCELED",
            "startDate": 1780041852,
            "cycleNumber": 3,
            "currentPeriod": {
              "start": 1785312252,
              "end": 1787990652
            },
            "cancellation": {
              "at": 1785669600,
              "reason": "MERCHANT"
            },
            "createdAt": 1780041521,
            "updatedAt": 1785669600
          }
        }
      },
      "SubscriptionExpiredEvent": {
        "description": "A `subscription.expired` event.",
        "allOf": [
          {
            "$ref": "#/components/schemas/WebhookEnvelope"
          },
          {
            "type": "object",
            "required": [
              "data"
            ],
            "properties": {
              "type": {
                "const": "subscription.expired"
              },
              "data": {
                "$ref": "#/components/schemas/Subscription"
              }
            }
          }
        ],
        "example": {
          "id": "0197c41d-88ae-7b6f-a2d1-0f5e7c3b9a44",
          "type": "subscription.expired",
          "timestamp": "2026-08-18T00:00:05.147903266Z",
          "sequence": 31,
          "resourceType": "subscription",
          "resourceId": "0197a02b-5cd1-7e94-bf62-8d31607ea4f5",
          "data": {
            "id": "0197a02b-5cd1-7e94-bf62-8d31607ea4f5",
            "planId": "019801a3-4d5e-7f80-9a1b-2c3d4e5f6071",
            "planName": "Pro Monthly",
            "amount": {
              "amount": "29.00",
              "asset": "USD"
            },
            "customerProfileId": "acct_5107",
            "email": "grace@example.com",
            "notifyUrl": "https://merchant.example/confirmo/webhook",
            "status": "EXPIRED",
            "startDate": 1755475200,
            "cycleNumber": 12,
            "currentPeriod": {
              "start": 1784332800,
              "end": 1787011200
            },
            "endsAt": 1787011200,
            "expiration": {
              "at": 1787011200,
              "reason": "TERM_REACHED"
            },
            "createdAt": 1755448930,
            "updatedAt": 1787011205
          }
        }
      },
      "SubscriptionPaymentSucceededEvent": {
        "description": "A `subscription.payment.succeeded` event.",
        "allOf": [
          {
            "$ref": "#/components/schemas/WebhookEnvelope"
          },
          {
            "type": "object",
            "required": [
              "data"
            ],
            "properties": {
              "type": {
                "const": "subscription.payment.succeeded"
              },
              "data": {
                "$ref": "#/components/schemas/Payment"
              }
            }
          }
        ],
        "example": {
          "id": "019811b5-2ac0-7f37-a48d-6e1c95b30772",
          "type": "subscription.payment.succeeded",
          "timestamp": "2026-06-29T08:04:18.330967104Z",
          "sequence": 3,
          "resourceType": "subscription",
          "resourceId": "019807c2-6f2a-7b41-9c58-2f9c1a4b8d21",
          "data": {
            "id": "019811b5-2a44-7c19-9f03-5ac7e1b04d68",
            "subscriptionId": "019807c2-6f2a-7b41-9c58-2f9c1a4b8d21",
            "billedAmount": {
              "amount": "29.00",
              "asset": "USD"
            },
            "paidAmount": {
              "amount": "29.02",
              "asset": "USDC"
            },
            "balance": {
              "asset": "USD",
              "amount": "29.00",
              "feeBase": "0.26",
              "feeProvider": "0.03",
              "feeNetwork": "0.00",
              "feeTotal": "0.29"
            },
            "status": "SUCCEEDED",
            "paidAt": 1782720258,
            "cycleNumber": 2,
            "createdAt": 1782720252
          }
        }
      },
      "SubscriptionPaymentFailedEvent": {
        "description": "A `subscription.payment.failed` event.",
        "allOf": [
          {
            "$ref": "#/components/schemas/WebhookEnvelope"
          },
          {
            "type": "object",
            "required": [
              "data"
            ],
            "properties": {
              "type": {
                "const": "subscription.payment.failed"
              },
              "data": {
                "$ref": "#/components/schemas/Payment"
              }
            }
          }
        ],
        "example": {
          "id": "01981e37-9b0c-70d5-8a76-c4e9f0128b3a",
          "type": "subscription.payment.failed",
          "timestamp": "2026-07-29T08:00:07.905412338Z",
          "sequence": 4,
          "resourceType": "subscription",
          "resourceId": "019807c2-6f2a-7b41-9c58-2f9c1a4b8d21",
          "data": {
            "id": "01981e37-9a55-7ee1-b03c-48f7d2610ba9",
            "subscriptionId": "019807c2-6f2a-7b41-9c58-2f9c1a4b8d21",
            "billedAmount": {
              "amount": "29.00",
              "asset": "USD"
            },
            "paidAmount": {
              "amount": "0.00",
              "asset": "USDC"
            },
            "status": "FAILED",
            "failure": {
              "reason": "INSUFFICIENT_BALANCE"
            },
            "cycleNumber": 3,
            "createdAt": 1785312000
          }
        }
      },
      "CustomerProfile": {
        "oneOf": [
          {
            "$ref": "#/components/schemas/IndividualProfile"
          },
          {
            "$ref": "#/components/schemas/CompanyProfile"
          },
          {
            "$ref": "#/components/schemas/ReferenceProfile"
          }
        ],
        "discriminator": {
          "propertyName": "type",
          "mapping": {
            "individual": "#/components/schemas/IndividualProfile",
            "company": "#/components/schemas/CompanyProfile",
            "reference": "#/components/schemas/ReferenceProfile"
          }
        },
        "description": "Optional customer profile, used to meet Travel Rule / AML obligations.\n`profileId` is returned as `Subscription.customerProfileId`; the rest\nof the profile is write-only and never returned by the API.\n\n**EU merchants:** merchants operating under the EU regulatory workflow\nmust supply a complete customer profile with every subscription, to meet\nTravel Rule / AML obligations. The profile must be an `individual` (with\n`firstName`, `lastName`, `dateOfBirth`, and a full address —\n`streetAddress`, `city`, `postalCode`, `country`) or a `company` (with\n`registeredName`, `registrationNumber`, and a full address); a\n`reference` profile is not accepted. A missing or incomplete profile is\nrejected with HTTP 422 and error code `CUSTOMER_PROFILE_REQUIRED`. For\nother merchants the profile remains optional.\n"
      },
      "CustomerProfileType": {
        "type": "string",
        "enum": [
          "individual",
          "company",
          "reference"
        ],
        "description": "Discriminator value for polymorphic CustomerProfile variants."
      },
      "CustomerProfileBase": {
        "type": "object",
        "required": [
          "type",
          "profileId"
        ],
        "properties": {
          "type": {
            "$ref": "#/components/schemas/CustomerProfileType"
          },
          "profileId": {
            "type": "string",
            "maxLength": 128,
            "description": "Your customer id. Returned as `Subscription.customerProfileId` and accepted by the `customerProfileId` filter when listing subscriptions."
          },
          "streetAddress": {
            "type": "string",
            "maxLength": 255
          },
          "city": {
            "type": "string",
            "maxLength": 255
          },
          "postalCode": {
            "type": "string",
            "maxLength": 64
          },
          "country": {
            "type": "string",
            "minLength": 2,
            "maxLength": 2,
            "description": "ISO 3166-1 alpha-2."
          }
        }
      },
      "IndividualProfile": {
        "allOf": [
          {
            "$ref": "#/components/schemas/CustomerProfileBase"
          },
          {
            "type": "object",
            "properties": {
              "firstName": {
                "type": "string",
                "maxLength": 255
              },
              "lastName": {
                "type": "string",
                "maxLength": 255
              },
              "dateOfBirth": {
                "type": "string",
                "format": "date"
              },
              "placeOfBirth": {
                "type": "string",
                "maxLength": 255
              }
            }
          }
        ]
      },
      "CompanyProfile": {
        "allOf": [
          {
            "$ref": "#/components/schemas/CustomerProfileBase"
          },
          {
            "type": "object",
            "properties": {
              "registeredName": {
                "type": "string",
                "maxLength": 255
              },
              "registrationNumber": {
                "type": "string",
                "maxLength": 255
              }
            }
          }
        ]
      },
      "ReferenceProfile": {
        "allOf": [
          {
            "$ref": "#/components/schemas/CustomerProfileBase"
          }
        ]
      },
      "ErrorResponse": {
        "type": "object",
        "description": "Canonical error envelope. Every non-2xx response uses this shape.",
        "required": [
          "code",
          "message"
        ],
        "properties": {
          "code": {
            "type": "string",
            "description": "Stable machine-readable error code.",
            "example": "SUBSCRIPTION_NOT_FOUND"
          },
          "message": {
            "type": "string",
            "description": "Human-readable summary."
          },
          "details": {
            "type": "object",
            "additionalProperties": true,
            "description": "Optional error-specific context."
          }
        }
      }
    },
    "responses": {
      "BadRequest": {
        "description": "Request is malformed or violates a validation rule.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            }
          }
        }
      },
      "Unauthorized": {
        "description": "Caller is unauthenticated.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            }
          }
        }
      },
      "Forbidden": {
        "description": "Caller is authenticated but not authorized for this resource.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            }
          }
        }
      },
      "InternalServerError": {
        "description": "Unexpected server error.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            }
          }
        }
      },
      "UnprocessableEntity": {
        "description": "Request parsed but violates a business rule or invariant.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            }
          }
        }
      },
      "NotFound": {
        "description": "Resource not found.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            }
          }
        }
      },
      "Conflict": {
        "description": "Request conflicts with the current resource state.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            }
          }
        }
      }
    }
  }
}