{"openapi":"3.1.0","info":{"title":"Pagos Express API","version":"0.0.1","description":"Pagos Express lets you create payment links that your customers pay on a branded page, and track the resulting payments, from any system that speaks HTTPS. Conventions follow Stripe: secret keys, idempotency keys, cursor pagination and RFC 7807 problem responses.\n\n## Authentication\n\nSend `Authorization: Bearer <secret key>` on every request. Keys are created in the dashboard under **API keys** and shown once.\n\n- `sk_test_…` keys work in **test mode**: links and payments are separate from live data and no real money moves. Test mode is free and unlimited.\n- `sk_live_…` keys work in **live mode**: real cards are charged into your connected Stripe account. Live keys require a verified business profile, and creating live links requires an active subscription.\n\nThe key selects both the account and the mode; every object carries a `livemode` flag. Keys are secrets: use them server-side only, never in browsers or mobile apps.\n\n## Getting started\n\n1. In the dashboard, connect your Stripe account (**Conexiones**). `GET /v1/connections` confirms an `active` Stripe connection for the key's mode.\n2. `POST /v1/payment_links` with an `Idempotency-Key` header. The response `url` is the page your customer pays on; share it however you like.\n3. Confirm payment with `GET /v1/payment_links/{id}` (status `paid`, `paidPaymentId`) or `GET /v1/payments?status=succeeded`. Only a payment in `succeeded` confirms funds. Never trust a browser redirect.\n4. Optionally, `POST /v1/payment_links/{id}/email` asks Pagos Express to email the link to your customer.\n\n```bash\ncurl https://api.pagos.express/v1/payment_links \\\n  -H \"Authorization: Bearer sk_test_...\" \\\n  -H \"Idempotency-Key: order-1042\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"title\":\"Consultoría\",\"items\":[{\"name\":\"Sesión\",\"quantity\":1,\"unitPriceCentavos\":150000}],\"currency\":\"MXN\"}'\n```\n\n## Conventions\n\n- **Amounts** are integers in minor units: MX$1,500.00 is `150000`. `currency` defaults to `MXN` and accepts two-decimal ISO 4217 codes Stripe can present. Totals below Stripe's per-currency minimum (MX$10.00, US$0.50) or above its maximum are rejected at creation.\n- **Idempotency**: `POST /v1/payment_links` and `POST /v1/payment_links/{id}/email` require an `Idempotency-Key` (1–255 visible ASCII characters; use one stable value per logical operation, such as your order id). Retrying with the same key and body returns the original response with `Idempotency-Replayed: true` for 24 hours. Same key with a different body → `422 idempotency_key_reuse`; a concurrent request with the same key → `409 request_in_flight` (retry shortly with the same key).\n- **Pagination** is cursor based: pass `limit` (1–100, default 20) and `starting_after=<last id>` while `has_more` is `true`. Lists are newest first.\n- **Errors** are `application/problem+json` with a stable `code`: `validation_error` (400, includes `errors[]` with field paths), `unauthorized` (401), `forbidden` (403), `subscription_required` (402), `not_found` (404), `link_already_paid` / `no_active_connection` / `request_in_flight` (409), `link_expired` (410), `idempotency_key_reuse` / `provider_rejected` (422), `rate_limited` (429, honor `Retry-After`), `provider_unavailable` (503, safe to retry with the same key) and `internal_error` (500).\n- **Rate limit**: 300 requests per minute per key by default; `X-RateLimit-Remaining` is returned on every response.\n- **Disabling** a link stops new payments. It never refunds an existing payment; refunds are handled in your Stripe dashboard.\n- **Editing**: `POST /v1/payment_links/{id}` changes the title, description, customer details, expiry or metadata of an active link (for example, to add bank transfer instructions). Items and amounts are immutable.\n- **Metadata**: `metadata` on a link holds your own flat key/value pairs (order id, CRM record…): up to 50 keys, string values up to 500 characters, numbers or booleans, no nesting. It is returned on the link and on its payments and included in every webhook payload; the payer never sees it. On update it replaces the whole object.\n- **Manual payments**: `POST /v1/payment_links/{id}/mark_paid` records money received outside the checkout (bank transfer, cash). It closes the link as `paid`, creates a `manual` payment in `succeeded` with your `note`, `method` and optional receipt, and fires the same webhooks as a card payment.\n\n## Webhooks\n\nInstead of polling, register an HTTPS endpoint in the dashboard (**Webhooks**) and receive a signed event for `payment.succeeded`, `payment.failed`, `payment.canceled`, `payment_link.paid` and `payment_link.disabled`. Each delivery carries `Pagos-Signature: t=<unix seconds>,v1=<hex HMAC-SHA256 of \"<t>.<raw body>\">`, signed with the `whsec_…` secret shown once when you register the endpoint. Verify over the raw bytes, reject timestamps older than five minutes, deduplicate on the event `id`, and respond 2xx within 10 seconds; failed deliveries retry for about 44 hours. `data.object` uses the same Payment and PaymentLink shapes as this API.\n\n## AI agents\n\nAgents can use the MCP server at `https://api.pagos.express/mcp` (test mode: `https://api.pagos.express/mcp/test`) instead of raw REST. It exposes these same operations as tools, authenticated with an API key or OAuth 2.1.","contact":{"name":"Pagos Express","url":"https://pagos.express"}},"servers":[{"url":"https://api.pagos.express"}],"tags":[{"name":"Payment links","description":"A payment link is a request for a fixed amount that your customer pays on a hosted, branded page. Create one per sale, share its `url`, and watch `status` move from `active` to `processing` (a checkout is open) to `paid`. Links can carry an `expiresAt` and your own `metadata`, and can be disabled at any time."},{"name":"Payments","description":"Every checkout attempt on a link becomes a payment. Only `succeeded` confirms that funds were captured; `pending` means nothing has been charged yet. Payment state is driven by signed Stripe webhooks and verified by a background worker; `sync` asks Stripe for the current state on demand."},{"name":"Email sharing","description":"Ask Pagos Express to email a payment link to your customer on your behalf, then follow its delivery state. Test-mode keys simulate delivery and never send real mail."},{"name":"Connections","description":"Payment providers connected to your account, per mode. A payment link can only be paid while a `stripe` connection is `active` for its mode. Connecting or reconnecting Stripe happens in the dashboard, never through an API key."},{"name":"Invoicing","description":"CFDI 4.0 invoices (facturas) for paid links, stamped through the account's CFDI Express connection with the issuing merchant chosen in the dashboard. Create links with `invoiceable: true` and SAT codes plus a `tax` treatment on every item; once a payment `succeeded`, the customer requests their invoice on the hosted page (`invoiceUrl`) or you request it for them with `POST /v1/payments/{id}/invoice`. Stamps are charged to the merchant's CFDI Express balance."}],"security":[{"apiKey":[]}],"components":{"schemas":{"Problem":{"type":"object","properties":{"type":{"type":"string","format":"uri"},"title":{"type":"string"},"status":{"type":"integer"},"code":{"type":"string","enum":["validation_error","unauthorized","forbidden","not_found","subscription_required","link_already_paid","link_expired","no_active_connection","request_in_flight","idempotency_key_reuse","provider_rejected","rate_limited","provider_unavailable","email_unavailable","billing_unavailable","invoicing_unavailable","invoicing_rejected","internal_error"]},"detail":{"type":"string"},"errors":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"message":{"type":"string"}},"required":["path","message"]}}},"required":["type","title","status","code"]},"InvoicingConnection":{"type":"object","properties":{"object":{"type":"string","enum":["invoicing_connection"]},"service":{"type":"string","enum":["cfdi_express"]},"status":{"type":"string","enum":["active","error","disconnected"]},"livemode":{"type":"boolean"},"displayName":{"type":["string","null"],"description":"CFDI Express user that authorized it."},"merchant":{"type":["object","null"],"properties":{"id":{"type":"string"},"rfc":{"type":["string","null"]},"legalName":{"type":["string","null"]},"defaultProductCode":{"type":["string","null"]},"defaultUnitCode":{"type":["string","null"]}},"required":["id","rfc","legalName","defaultProductCode","defaultUnitCode"],"description":"Issuing merchant (emisor); `null` until the owner chooses one."},"ready":{"type":"boolean","description":"Links can be made `invoiceable`: connected and with a merchant chosen."},"lastError":{"type":["string","null"]},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}},"required":["object","service","status","livemode","displayName","merchant","ready","lastError","createdAt","updatedAt"]},"PaymentInvoice":{"type":["object","null"],"properties":{"object":{"type":"string","enum":["payment_invoice"]},"id":{"type":"string"},"paymentId":{"type":"string"},"livemode":{"type":"boolean"},"status":{"type":"string","enum":["requested","stamping","stamped","failed","cancel_pending","cancelled"],"description":"`requested`: accepted, waiting for CFDI Express (retrying or out of stamp balance) · `stamping` · `stamped`: legally issued, see `uuid` · `failed`: rejected, see `failureReason`; request it again with corrected data · `cancel_pending` · `cancelled`."},"uuid":{"type":["string","null"],"description":"SAT folio fiscal once stamped."},"totalCentavos":{"type":["integer","null"]},"formaPago":{"type":["string","null"],"description":"SAT c_FormaPago stamped on the CFDI."},"receiver":{"type":"object","properties":{"rfc":{"type":"string"},"name":{"type":"string"},"zip":{"type":"string"},"regimenFiscal":{"type":"string"},"usoCfdi":{"type":"string"},"email":{"type":["string","null"]}},"required":["rfc","name","zip","regimenFiscal","usoCfdi","email"]},"failureCode":{"type":["string","null"]},"failureReason":{"type":["string","null"]},"cancelMotivo":{"type":["string","null"]},"stampedAt":{"type":["string","null"],"format":"date-time"},"cancelledAt":{"type":["string","null"],"format":"date-time"},"createdAt":{"type":"string","format":"date-time"}},"required":["object","id","paymentId","livemode","status","uuid","totalCentavos","formaPago","receiver","failureCode","failureReason","cancelMotivo","stampedAt","cancelledAt","createdAt"]},"InvoiceRequest":{"type":"object","properties":{"rfc":{"type":"string","example":"EKU9003173C9"},"name":{"type":"string","description":"Legal name exactly as on the Constancia de Situación Fiscal.","example":"ESCUELA KEMPER URGATE"},"zip":{"type":"string","description":"Postal code of the domicilio fiscal.","example":"26015"},"regimenFiscal":{"type":"string","example":"601"},"usoCfdi":{"type":"string","example":"G03"},"email":{"type":"string","format":"email","description":"Where to send the stamped invoice."},"formaPago":{"type":"string","description":"SAT c_FormaPago. Needed only when the payment method does not settle it (for example a card whose credit/debit type the provider did not report).","example":"04"}},"required":["rfc","name","zip","regimenFiscal","usoCfdi"]},"CancelInvoice":{"type":"object","properties":{"motivo":{"type":"string","enum":["02","03"],"description":"`02` issued with errors, no replacement · `03` the operation did not happen."}},"required":["motivo"],"additionalProperties":false},"PaymentLink":{"allOf":[{"$ref":"#/components/schemas/CreatedPaymentLink"},{"type":"object","properties":{"slug":{"type":"string"},"title":{"type":"string"},"description":{"type":["string","null"]},"items":{"type":"array","items":{"$ref":"#/components/schemas/PaymentItem"}},"customerEmail":{"type":["string","null"]},"customerName":{"type":["string","null"]},"createdAt":{"type":"string","format":"date-time"},"paidPaymentId":{"type":["string","null"],"description":"Id of the successful payment once `status` is `paid`."},"channel":{"type":"string","description":"Where the link was created: `api`, `dashboard` or `mcp`."},"providers":{"type":"array","items":{"type":"string","enum":["stripe","ecartpay"],"description":"`stripe` or `ecartpay`."},"maxItems":2,"description":"Providers the customer may pay with, in the order offered on the payment page. Each one must have an `active` connection in this mode. Omit or send `[]` to offer every connected provider at checkout time.","example":["stripe","ecartpay"]},"invoiceable":{"type":"boolean","description":"Lets the customer request a CFDI (factura) once paid. Requires a CFDI Express connection with an issuing merchant in this mode, and `satProductCode`, `satUnitCode` and `tax` on every item."}},"required":["slug","title","description","items","customerEmail","customerName","createdAt","paidPaymentId","channel","providers","invoiceable"]}]},"Metadata":{"type":"object","additionalProperties":{"anyOf":[{"type":"string","maxLength":500},{"type":"number"},{"type":"boolean"}]},"description":"Your own key/value pairs (order id, CRM record, campaign…). Up to 50 keys of 40 characters; values are strings (≤ 500 characters), numbers or booleans. Nested objects and arrays are rejected. Returned on the link and its payments and included in every webhook payload; never shown to the payer.","example":{"order_id":"1042","source":"shopify","vip":true}},"PaymentItem":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":200,"description":"Shown on the payment page.","example":"Sesión de consultoría"},"quantity":{"type":"integer","minimum":1,"maximum":999,"example":1},"unitPriceCentavos":{"type":"integer","minimum":1,"maximum":999999999,"description":"Unit price in minor units (centavos for MXN).","example":150000},"satProductCode":{"type":"string","pattern":"^\\d{8}$","description":"SAT c_ClaveProdServ (8 digits). Required on every item of an `invoiceable` link.","example":"80111600"},"satUnitCode":{"type":"string","pattern":"^[A-Z0-9]{1,5}$","description":"SAT c_ClaveUnidad (`E48` service, `H87` piece…). Required when `invoiceable`.","example":"E48"},"tax":{"type":"string","enum":["iva_included","iva_added","exempt"],"description":"How the price relates to 16 % IVA. `iva_included` (default): the price already includes it. `iva_added`: IVA is charged on top, so the link total grows by it. `exempt`: no IVA. Required when `invoiceable`.","example":"iva_included"}},"required":["name","quantity","unitPriceCentavos"]},"CreatedPaymentLink":{"type":"object","properties":{"object":{"type":"string","enum":["payment_link"]},"id":{"type":"string"},"livemode":{"type":"boolean"},"status":{"type":"string","enum":["active","processing","paid","expired","disabled"],"description":"`active`: can be paid · `processing`: a customer has a checkout open · `paid`: settled, see `paidPaymentId` · `expired`: past `expiresAt` · `disabled`: closed by you."},"url":{"type":"string","format":"uri","description":"Hosted payment page to share with your customer."},"amountCentavos":{"type":"integer","exclusiveMinimum":0,"maximum":2147483647,"description":"Total in minor units (centavos for MXN).","example":150000},"currency":{"type":"string","pattern":"^[A-Z]{3}$","description":"ISO 4217 code.","example":"MXN"},"expiresAt":{"type":["string","null"],"format":"date-time"},"metadata":{"$ref":"#/components/schemas/Metadata"}},"required":["object","id","livemode","status","url","amountCentavos","currency","expiresAt","metadata"]},"UpdatePaymentLink":{"type":"object","properties":{"title":{"type":"string","minLength":1,"maxLength":200},"description":{"type":["string","null"],"maxLength":2000,"description":"Text under the title on the payment page; `null` clears it."},"customerEmail":{"type":["string","null"],"format":"email"},"customerName":{"type":["string","null"],"minLength":1,"maxLength":200},"expiresAt":{"type":["string","null"],"format":"date-time","description":"New expiry, or `null` to remove it. Must be in the future."},"metadata":{"type":["object","null"],"additionalProperties":{"anyOf":[{"type":"string","maxLength":500},{"type":"number"},{"type":"boolean"}]},"description":"Replaces the whole metadata object; keys you omit are removed. `null` or `{}` clears it.","example":{"order_id":"1042","source":"shopify","vip":true}},"providers":{"type":"array","items":{"type":"string","enum":["stripe","ecartpay"],"description":"`stripe` or `ecartpay`."},"maxItems":2,"description":"Replaces the list of providers the customer may pay with; `[]` offers every connected provider.","example":["stripe","ecartpay"]},"invoiceable":{"type":"boolean","description":"Lets the customer request a CFDI (factura) once paid. Requires a CFDI Express connection with an issuing merchant in this mode, and `satProductCode`, `satUnitCode` and `tax` on every item."}},"additionalProperties":false},"MarkPaymentLinkPaid":{"type":"object","properties":{"method":{"type":"string","enum":["card","oxxo_cash","spei_transfer","other"],"default":"other","description":"How the customer paid outside the checkout."},"note":{"type":"string","maxLength":1000,"description":"Free text: bank, reference number, who confirmed it."},"paidAt":{"type":"string","format":"date-time","description":"When the money was received. Defaults to now; cannot be in the future."},"reference":{"type":"string","description":"Optional receipt as a `data:` URL (PNG, JPEG, WebP or PDF, up to 700 KB). Retrieved later from `GET /payments/{id}/reference`."}},"additionalProperties":false},"Payment":{"type":"object","properties":{"object":{"type":"string","enum":["payment"]},"id":{"type":"string"},"reconcileAfter":{"type":["string","null"],"format":"date-time","description":"When the background worker will next verify a pending payment with the provider."},"reconcileError":{"type":["string","null"],"description":"Why the last verification was deferred, if it was."},"linkId":{"type":"string"},"livemode":{"type":"boolean"},"status":{"type":"string","enum":["pending","succeeded","failed","refunded","canceled"],"description":"Only `succeeded` confirms funds. `pending`: checkout open or awaiting confirmation · `failed`/`canceled`: no charge · `refunded`: returned to the customer."},"provider":{"type":"string","enum":["stripe","ecartpay","manual"],"description":"`manual` when the merchant recorded the payment themselves."},"providerRef":{"type":"string","description":"Provider-side id: Stripe Checkout Session (`cs_…`) or eCart Pay order id, `creating:<payment id>` while creation is still in flight, or `manual:<uuid>`."},"method":{"type":["string","null"],"description":"Payment method reported by the provider, when known."},"amountCentavos":{"type":"integer","exclusiveMinimum":0,"maximum":2147483647,"description":"Total in minor units (centavos for MXN).","example":150000},"currency":{"type":"string","pattern":"^[A-Z]{3}$","description":"ISO 4217 code.","example":"MXN"},"customerEmail":{"type":["string","null"]},"customerName":{"type":["string","null"]},"paidAt":{"type":["string","null"],"format":"date-time"},"createdAt":{"type":"string","format":"date-time"},"note":{"type":["string","null"],"description":"Merchant note on a manually recorded payment."},"hasReference":{"type":"boolean","description":"Whether a receipt is available at `GET /payments/{id}/reference`."},"metadata":{"allOf":[{"$ref":"#/components/schemas/Metadata"},{"description":"The `metadata` of the payment link this payment belongs to."}]}},"required":["object","id","reconcileAfter","reconcileError","linkId","livemode","status","provider","providerRef","method","amountCentavos","currency","customerEmail","customerName","paidAt","createdAt","note","hasReference","metadata"]},"Connection":{"type":"object","properties":{"id":{"type":"string"},"provider":{"type":"string","enum":["stripe","ecartpay"]},"status":{"type":"string","enum":["active","error","disconnected"],"description":"Links can only be paid while this is `active`."},"livemode":{"type":"boolean"},"externalAccountId":{"type":["string","null"],"description":"Provider-side account id (Stripe `acct_…`, eCart Pay account id)."},"displayName":{"type":["string","null"]},"lastError":{"type":["string","null"]},"lastCheckedAt":{"type":["string","null"],"format":"date-time"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}},"required":["id","provider","status","livemode","externalAccountId","displayName","lastError","lastCheckedAt","createdAt","updatedAt"]},"CreatePaymentLink":{"type":"object","properties":{"title":{"type":"string","minLength":1,"maxLength":200,"description":"Heading of the payment page.","example":"Consultoría septiembre"},"description":{"type":"string","maxLength":2000,"description":"Optional text shown under the title."},"items":{"type":"array","items":{"$ref":"#/components/schemas/PaymentItem"},"minItems":1,"maxItems":20,"description":"Line items; the link total is their sum."},"currency":{"type":"string","pattern":"^[A-Za-z]{3}$","default":"MXN","description":"ISO 4217 code. Only two-decimal Stripe presentment currencies are accepted; zero-decimal (JPY, KRW) and three-decimal (KWD) currencies are rejected. Availability depends on the connected Stripe account.","example":"MXN"},"customerEmail":{"type":"string","format":"email","description":"Pre-fills the customer's email at checkout."},"customerName":{"type":"string","minLength":1,"maxLength":200},"expiresAt":{"type":"string","format":"date-time","description":"ISO 8601. After this instant the link can no longer be paid; checkouts cannot start within 30 minutes of it."},"metadata":{"$ref":"#/components/schemas/Metadata"},"providers":{"type":"array","items":{"type":"string","enum":["stripe","ecartpay"],"description":"`stripe` or `ecartpay`."},"maxItems":2,"default":[],"description":"Providers the customer may pay with, in the order offered on the payment page. Each one must have an `active` connection in this mode. Omit or send `[]` to offer every connected provider at checkout time.","example":["stripe","ecartpay"]},"invoiceable":{"type":"boolean","default":false,"description":"Lets the customer request a CFDI (factura) once paid. Requires a CFDI Express connection with an issuing merchant in this mode, and `satProductCode`, `satUnitCode` and `tax` on every item."}},"required":["title","items"]}},"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"Secret API key created in the dashboard: `sk_test_…` selects test mode, `sk_live_…` live mode. Send it as `Authorization: Bearer <key>` from your server only."}}},"paths":{"/v1/payment_links/{id}/email":{"post":{"operationId":"sendPaymentLinkEmail","summary":"Email a payment link","description":"Queues an email carrying the link to `to`, sent by Pagos Express on your behalf with your branding. Requires `Idempotency-Key`. Returns 202 with the delivery record; `queued` is not proof of delivery, so follow up with the deliveries list. Test-mode keys simulate sending. Limited to 3 emails per link per minute.","tags":["Email sharing"],"security":[{"apiKey":[]}],"parameters":[{"schema":{"type":"string","minLength":1},"required":true,"name":"id","in":"path"},{"schema":{"type":"string","minLength":1,"maxLength":255,"pattern":"^[\\x21-\\x7e]+$","description":"A unique key per operation. Retried successful requests return the original response for 24 hours. Reusing a key with a different payload/operation returns 422; concurrent requests return 409. Scope: account + test/live mode, shared across REST and dashboard."},"required":true,"description":"A unique key per operation. Retried successful requests return the original response for 24 hours. Reusing a key with a different payload/operation returns 422; concurrent requests return 409. Scope: account + test/live mode, shared across REST and dashboard.","name":"idempotency-key","in":"header"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"to":{"type":"string","maxLength":254,"format":"email"}},"required":["to"],"additionalProperties":false}}}},"responses":{"202":{"description":"Success","headers":{"Idempotency-Replayed":{"schema":{"type":"string","enum":["true","false"]},"description":"Whether the original successful response was replayed."}},"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"linkId":{"type":["string","null"]},"to":{"type":"string","format":"email"},"livemode":{"type":"boolean"},"status":{"type":"string","enum":["queued","sending","unknown","simulated","sent","delivered","bounced","failed"]},"subject":{"type":"string"},"error":{"type":["string","null"]},"createdAt":{"type":"string","format":"date-time"},"sentAt":{"type":["string","null"],"format":"date-time"},"deliveredAt":{"type":["string","null"],"format":"date-time"},"bouncedAt":{"type":["string","null"],"format":"date-time"},"openedAt":{"type":["string","null"],"format":"date-time"},"clickedAt":{"type":["string","null"],"format":"date-time"},"complainedAt":{"type":["string","null"],"format":"date-time"},"unsubscribedAt":{"type":["string","null"],"format":"date-time"},"temporaryFailedAt":{"type":["string","null"],"format":"date-time"}},"required":["id","linkId","to","livemode","status","subject","error","createdAt","sentAt","deliveredAt","bouncedAt","openedAt","clickedAt","complainedAt","unsubscribedAt","temporaryFailedAt"]}}}},"default":{"description":"Request failed (see problem code). Authentication and validation run before idempotency replay.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/payment_links/{id}/emails":{"get":{"operationId":"listPaymentLinkEmails","summary":"List email deliveries for a link","description":"Latest 50 deliveries, newest first, with provider delivery states (`sent`, `delivered`, `bounced`, …) and recipient activity timestamps.","tags":["Email sharing"],"security":[{"apiKey":[]}],"parameters":[{"schema":{"type":"string","minLength":1},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"linkId":{"type":["string","null"]},"to":{"type":"string","format":"email"},"livemode":{"type":"boolean"},"status":{"type":"string","enum":["queued","sending","unknown","simulated","sent","delivered","bounced","failed"]},"subject":{"type":"string"},"error":{"type":["string","null"]},"createdAt":{"type":"string","format":"date-time"},"sentAt":{"type":["string","null"],"format":"date-time"},"deliveredAt":{"type":["string","null"],"format":"date-time"},"bouncedAt":{"type":["string","null"],"format":"date-time"},"openedAt":{"type":["string","null"],"format":"date-time"},"clickedAt":{"type":["string","null"],"format":"date-time"},"complainedAt":{"type":["string","null"],"format":"date-time"},"unsubscribedAt":{"type":["string","null"],"format":"date-time"},"temporaryFailedAt":{"type":["string","null"],"format":"date-time"}},"required":["id","linkId","to","livemode","status","subject","error","createdAt","sentAt","deliveredAt","bouncedAt","openedAt","clickedAt","complainedAt","unsubscribedAt","temporaryFailedAt"]}}},"required":["data"]}}}},"default":{"description":"Request failed (see problem code). Authentication and validation run before idempotency replay.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/invoicing/connection":{"get":{"operationId":"getInvoicingConnection","summary":"Retrieve the CFDI Express connection","description":"The account's CFDI Express connection in this key's mode and its issuing merchant. `ready` means links can be created with `invoiceable: true`. Connecting and choosing the merchant happen in the dashboard. 404 when CFDI Express was never connected in this mode.","tags":["Invoicing"],"security":[{"apiKey":[]}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InvoicingConnection"}}}},"default":{"description":"Request failed (see problem code). Authentication and validation run before idempotency replay.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/invoicing/catalogs":{"get":{"operationId":"getInvoicingCatalogs","summary":"SAT régimen fiscal and uso CFDI catalogs","description":"The SAT catalogs a receiver's `regimenFiscal` and `usoCfdi` must come from, read through the account's CFDI Express connection in this mode. `fisica` / `moral` say which kind of taxpayer each value applies to; tell them apart by RFC length (13 física, 12 moral).","tags":["Invoicing"],"security":[{"apiKey":[]}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"regimenes":{"type":"array","items":{"type":"object","properties":{"clave":{"type":"string"},"nombre":{"type":"string"},"fisica":{"type":"boolean","description":"Applies to personas físicas (13-character RFC)."},"moral":{"type":"boolean","description":"Applies to personas morales (12-character RFC)."}},"required":["clave","nombre","fisica","moral"]}},"usos":{"type":"array","items":{"type":"object","properties":{"clave":{"type":"string"},"nombre":{"type":"string"},"fisica":{"type":"boolean","description":"Applies to personas físicas (13-character RFC)."},"moral":{"type":"boolean","description":"Applies to personas morales (12-character RFC)."}},"required":["clave","nombre","fisica","moral"]}}},"required":["regimenes","usos"]}}}},"default":{"description":"Request failed (see problem code). Authentication and validation run before idempotency replay.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/payments/{id}/invoice":{"get":{"operationId":"getPaymentInvoice","summary":"Invoicing state of a payment","description":"Whether the payment can be invoiced, the customer's self-invoice page (`invoiceUrl`), the SAT formas de pago that fit how it was paid, and its latest invoice.","tags":["Invoicing"],"security":[{"apiKey":[]}],"parameters":[{"schema":{"type":"string","minLength":1},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"object":{"type":"string","enum":["payment_invoicing"]},"invoiceable":{"type":"boolean"},"invoiceUrl":{"type":["string","null"],"format":"uri"},"formasPago":{"type":"object","properties":{"suggested":{"type":["string","null"]},"allowed":{"type":"array","items":{"type":"object","properties":{"code":{"type":"string"},"label":{"type":"string"}},"required":["code","label"]}}},"required":["suggested","allowed"]},"invoice":{"$ref":"#/components/schemas/PaymentInvoice"}},"required":["object","invoiceable","invoiceUrl","formasPago","invoice"]}}}},"default":{"description":"Request failed (see problem code). Authentication and validation run before idempotency replay.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"post":{"operationId":"invoicePayment","summary":"Invoice a payment","description":"Stamps a CFDI for a `succeeded` payment of an `invoiceable` link with the receiver's tax data. Returns the open invoice unchanged if one exists. Stamping usually finishes within seconds; otherwise `status` stays `stamping` (or `requested` while CFDI Express is unavailable or out of balance) and a `payment.invoiced` webhook follows.","tags":["Invoicing"],"security":[{"apiKey":[]}],"parameters":[{"schema":{"type":"string","minLength":1},"required":true,"name":"id","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/InvoiceRequest"}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaymentInvoice"}}}},"default":{"description":"Request failed (see problem code). Authentication and validation run before idempotency replay.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/payments/{id}/invoice/cancel":{"post":{"operationId":"cancelPaymentInvoice","summary":"Cancel a payment's invoice","description":"Asks the SAT to cancel the stamped invoice. The status becomes `cancel_pending` until the SAT acknowledges it, then `cancelled` (`payment.invoice_cancelled` webhook). Cancelling costs one stamp. It does not refund the payment.","tags":["Invoicing"],"security":[{"apiKey":[]}],"parameters":[{"schema":{"type":"string","minLength":1},"required":true,"name":"id","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CancelInvoice"}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaymentInvoice"}}}},"default":{"description":"Request failed (see problem code). Authentication and validation run before idempotency replay.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/payments/{id}/invoice/files":{"get":{"operationId":"getPaymentInvoiceFiles","summary":"Download links of a payment's invoice","description":"Signed PDF and XML URLs from CFDI Express. They expire after 15 minutes; call again for fresh ones.","tags":["Invoicing"],"security":[{"apiKey":[]}],"parameters":[{"schema":{"type":"string","minLength":1},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string"},"pdfUrl":{"type":["string","null"],"format":"uri"},"xmlUrl":{"type":["string","null"],"format":"uri"},"expiresInSeconds":{"type":"integer"}},"required":["status","pdfUrl","xmlUrl","expiresInSeconds"]}}}},"default":{"description":"Request failed (see problem code). Authentication and validation run before idempotency replay.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/payment_links":{"get":{"operationId":"listPaymentLinks","summary":"List payment links","description":"Newest first. Filter with `status`; page with `limit` and `starting_after` while `has_more` is true.","tags":["Payment links"],"security":[{"apiKey":[]}],"parameters":[{"schema":{"type":"integer","minimum":1,"maximum":100,"default":20},"required":false,"name":"limit","in":"query"},{"schema":{"type":"string","minLength":1},"required":false,"name":"starting_after","in":"query"},{"schema":{"type":"string","enum":["active","processing","paid","expired","disabled"],"description":"`active`: can be paid · `processing`: a customer has a checkout open · `paid`: settled, see `paidPaymentId` · `expired`: past `expiresAt` · `disabled`: closed by you."},"required":false,"description":"`active`: can be paid · `processing`: a customer has a checkout open · `paid`: settled, see `paidPaymentId` · `expired`: past `expiresAt` · `disabled`: closed by you.","name":"status","in":"query"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"object":{"type":"string","enum":["list"]},"data":{"type":"array","items":{"$ref":"#/components/schemas/PaymentLink"}},"has_more":{"type":"boolean"}},"required":["object","data","has_more"]}}}},"default":{"description":"Request failed (see problem code). Authentication and validation run before idempotency replay.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"post":{"operationId":"createPaymentLink","summary":"Create a payment link","description":"Creates a link for the items' total in `currency` and returns the `url` your customer pays on. Requires `Idempotency-Key`; retrying with the same key and body returns the original link. `providers` picks which connected providers (Stripe, eCart Pay) the customer may use; each must have an `active` connection in this mode, and the link can only be paid while one is. Creating live-mode links requires an active subscription.","tags":["Payment links"],"security":[{"apiKey":[]}],"parameters":[{"schema":{"type":"string","minLength":1,"maxLength":255,"pattern":"^[\\x21-\\x7e]+$","description":"A unique key per operation. Retried successful requests return the original response for 24 hours. Reusing a key with a different payload/operation returns 422; concurrent requests return 409. Scope: account + test/live mode, shared across REST and dashboard."},"required":true,"description":"A unique key per operation. Retried successful requests return the original response for 24 hours. Reusing a key with a different payload/operation returns 422; concurrent requests return 409. Scope: account + test/live mode, shared across REST and dashboard.","name":"idempotency-key","in":"header"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreatePaymentLink"}}}},"responses":{"201":{"description":"Success","headers":{"Idempotency-Replayed":{"schema":{"type":"string","enum":["true","false"]},"description":"Whether the original successful response was replayed."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreatedPaymentLink"}}}},"default":{"description":"Request failed (see problem code). Authentication and validation run before idempotency replay.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/payment_links/{id}":{"get":{"operationId":"getPaymentLink","summary":"Retrieve a payment link","description":"Current state of one link. Once paid, `status` is `paid` and `paidPaymentId` points at the successful payment.","tags":["Payment links"],"security":[{"apiKey":[]}],"parameters":[{"schema":{"type":"string","minLength":1},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaymentLink"}}}},"default":{"description":"Request failed (see problem code). Authentication and validation run before idempotency replay.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}},"post":{"operationId":"updatePaymentLink","summary":"Update a payment link","description":"Changes the title, description, customer details, expiry, `metadata`, `providers` or `invoiceable` of an `active` (or `processing`) link, for example to add bank transfer instructions after sharing it. `metadata` and `providers` replace the whole value. Items and amounts are immutable. Paid, expired and disabled links cannot be edited.","tags":["Payment links"],"security":[{"apiKey":[]}],"parameters":[{"schema":{"type":"string","minLength":1},"required":true,"name":"id","in":"path"},{"schema":{"type":"string","minLength":1,"maxLength":255,"pattern":"^[\\x21-\\x7e]+$","description":"A unique key per operation. Retried successful requests return the original response for 24 hours. Reusing a key with a different payload/operation returns 422; concurrent requests return 409. Scope: account + test/live mode, shared across REST and dashboard."},"required":false,"description":"A unique key per operation. Retried successful requests return the original response for 24 hours. Reusing a key with a different payload/operation returns 422; concurrent requests return 409. Scope: account + test/live mode, shared across REST and dashboard.","name":"idempotency-key","in":"header"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdatePaymentLink"}}}},"responses":{"200":{"description":"Success","headers":{"Idempotency-Replayed":{"schema":{"type":"string","enum":["true","false"]},"description":"Whether the original successful response was replayed."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaymentLink"}}}},"default":{"description":"Request failed (see problem code). Authentication and validation run before idempotency replay.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/payment_links/{id}/mark_paid":{"post":{"operationId":"markPaymentLinkPaid","summary":"Record a payment received outside the checkout","description":"Closes the link as `paid` with a `manual` payment in `succeeded`, for money received by bank transfer, cash or any other channel. Records how it was paid, an optional note and an optional receipt, and fires `payment.succeeded` and `payment_link.paid` like a card payment. Works on active, expired and disabled links; a link with a checkout in progress returns `409 request_in_flight`, an already paid link `409 link_already_paid`. Nothing is charged and nothing is refunded.","tags":["Payment links"],"security":[{"apiKey":[]}],"parameters":[{"schema":{"type":"string","minLength":1},"required":true,"name":"id","in":"path"},{"schema":{"type":"string","minLength":1,"maxLength":255,"pattern":"^[\\x21-\\x7e]+$","description":"A unique key per operation. Retried successful requests return the original response for 24 hours. Reusing a key with a different payload/operation returns 422; concurrent requests return 409. Scope: account + test/live mode, shared across REST and dashboard."},"required":false,"description":"A unique key per operation. Retried successful requests return the original response for 24 hours. Reusing a key with a different payload/operation returns 422; concurrent requests return 409. Scope: account + test/live mode, shared across REST and dashboard.","name":"idempotency-key","in":"header"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MarkPaymentLinkPaid"}}}},"responses":{"200":{"description":"Success","headers":{"Idempotency-Replayed":{"schema":{"type":"string","enum":["true","false"]},"description":"Whether the original successful response was replayed."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaymentLink"}}}},"default":{"description":"Request failed (see problem code). Authentication and validation run before idempotency replay.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/payment_links/{id}/disable":{"post":{"operationId":"disablePaymentLink","summary":"Disable a payment link","description":"Stops new payments on an active link and returns it as `disabled`. If a customer has a checkout open, the Stripe session is expired first; a link that was already paid returns `409 link_already_paid`. Disabling never refunds.","tags":["Payment links"],"security":[{"apiKey":[]}],"parameters":[{"schema":{"type":"string","minLength":1},"required":true,"name":"id","in":"path"},{"schema":{"type":"string","minLength":1,"maxLength":255,"pattern":"^[\\x21-\\x7e]+$","description":"A unique key per operation. Retried successful requests return the original response for 24 hours. Reusing a key with a different payload/operation returns 422; concurrent requests return 409. Scope: account + test/live mode, shared across REST and dashboard."},"required":false,"description":"A unique key per operation. Retried successful requests return the original response for 24 hours. Reusing a key with a different payload/operation returns 422; concurrent requests return 409. Scope: account + test/live mode, shared across REST and dashboard.","name":"idempotency-key","in":"header"}],"responses":{"200":{"description":"Success","headers":{"Idempotency-Replayed":{"schema":{"type":"string","enum":["true","false"]},"description":"Whether the original successful response was replayed."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaymentLink"}}}},"default":{"description":"Request failed (see problem code). Authentication and validation run before idempotency replay.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/payments":{"get":{"operationId":"listPayments","summary":"List payments","description":"Newest first. `status=succeeded` lists confirmed payments; page with `limit` and `starting_after`.","tags":["Payments"],"security":[{"apiKey":[]}],"parameters":[{"schema":{"type":"integer","minimum":1,"maximum":100,"default":20},"required":false,"name":"limit","in":"query"},{"schema":{"type":"string","minLength":1},"required":false,"name":"starting_after","in":"query"},{"schema":{"type":"string","enum":["pending","succeeded","failed","refunded","canceled"],"description":"Only `succeeded` confirms funds. `pending`: checkout open or awaiting confirmation · `failed`/`canceled`: no charge · `refunded`: returned to the customer."},"required":false,"description":"Only `succeeded` confirms funds. `pending`: checkout open or awaiting confirmation · `failed`/`canceled`: no charge · `refunded`: returned to the customer.","name":"status","in":"query"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"object":{"type":"string","enum":["list"]},"data":{"type":"array","items":{"$ref":"#/components/schemas/Payment"}},"has_more":{"type":"boolean"}},"required":["object","data","has_more"]}}}},"default":{"description":"Request failed (see problem code). Authentication and validation run before idempotency replay.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/payments/{id}/sync":{"post":{"operationId":"syncPayment","summary":"Verify a payment with Stripe","description":"Asks Stripe for the current state of a pending payment, for example when you suspect a webhook was missed. `outcome` is `updated` when the state changed, `pending` when Stripe still reports an open checkout, `retrying` when Stripe was unreachable, or `needs_review` when the record needs a human. Unknown outcomes never release the customer's reservation. A verification already in progress returns `409 request_in_flight`.","tags":["Payments"],"security":[{"apiKey":[]}],"parameters":[{"schema":{"type":"string","minLength":1},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"paymentId":{"type":"string"},"status":{"type":"string"},"outcome":{"type":"string","enum":["updated","pending","retrying","needs_review"]},"detail":{"type":"string"}},"required":["paymentId","status","outcome"]}}}},"default":{"description":"Request failed (see problem code). Authentication and validation run before idempotency replay.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/payments/{id}/reference":{"get":{"operationId":"getPaymentReference","summary":"Download the receipt attached to a manual payment","description":"The receipt image or PDF attached when the payment was recorded manually. 404 when the payment has no receipt.","tags":["Payments"],"security":[{"apiKey":[]}],"parameters":[{"schema":{"type":"string","minLength":1},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Success","content":{"application/octet-stream":{"schema":{"type":"string","format":"binary"}}}},"default":{"description":"Request failed (see problem code). Authentication and validation run before idempotency replay.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/payments/{id}":{"get":{"operationId":"getPayment","summary":"Retrieve a payment","description":"Only `succeeded` confirms funds. `reconcileAfter` and `reconcileError` show when the background worker will next verify a pending payment with Stripe and why the last check was deferred.","tags":["Payments"],"security":[{"apiKey":[]}],"parameters":[{"schema":{"type":"string","minLength":1},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Payment"}}}},"default":{"description":"Request failed (see problem code). Authentication and validation run before idempotency replay.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/v1/connections":{"get":{"operationId":"listConnections","summary":"List connected payment providers","description":"Providers connected to your account in this key's mode. A payment link can be paid only while one of its `providers` has an `active` connection; use this to check readiness before creating links. Connecting or reconnecting a provider is done in the dashboard.","tags":["Connections"],"security":[{"apiKey":[]}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"object":{"type":"string","enum":["list"]},"data":{"type":"array","items":{"$ref":"#/components/schemas/Connection"}},"has_more":{"type":"boolean"}},"required":["object","data","has_more"]}}}},"default":{"description":"Request failed (see problem code). Authentication and validation run before idempotency replay.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}}},"webhooks":{}}