{"openapi":"3.1.0","info":{"title":"RevKeen API","version":"2026-05-01","description":"RevKeen public REST API for payments, subscriptions, invoicing, and billing.\n\nAuthentication: send your API key in the `x-api-key` header.\nProduction uses `rk_live_*`; staging uses `rk_sandbox_*`.\n\nBase URLs:\n- Production: `https://api.revkeen.com/v2`\n- Staging: `https://staging-api.revkeen.com/v2`\n- Mock: `https://mock-api.revkeen.com/v2`\n\nFor the human-friendly reference, open `https://docs.revkeen.com/docs/api-reference`.","contact":{"name":"RevKeen Support","email":"support@revkeen.com","url":"https://docs.revkeen.com"},"license":{"name":"Proprietary","url":"https://revkeen.com/terms"},"x-logo":{"url":"https://revkeen.com/logo.svg","altText":"RevKeen"},"x-scalar-sdk-installation":[{"lang":"TypeScript","description":"Install the official [TypeScript SDK](https://www.npmjs.com/package/@revkeen/sdk) from npm.","source":"npm install @revkeen/sdk"},{"lang":"Go","description":"Check the [Go SDK release status](https://docs.revkeen.com/docs/sdks/go) before installing.","source":"See https://docs.revkeen.com/docs/sdks/go for verified release availability"},{"lang":"PHP","description":"Check the [PHP SDK release status](https://docs.revkeen.com/docs/sdks/php) before installing.","source":"See https://docs.revkeen.com/docs/sdks/php for verified release availability"}]},"x-tagGroups":[{"name":"🤖 Assistant & Automations","tags":["Automations"]},{"name":"💳 Core Payments","tags":["Payment Intents","Setup Intents","Charges","Payments","Refunds","Voids"]},{"name":"🔗 Checkout","tags":["Payment Links","Checkout Sessions","Cart Sessions","Cart API Keys","Storefront"]},{"name":"📋 Billing & Subscriptions","tags":["Invoices","Credit Notes","Subscriptions","Subscription Schedules","Orders","Billing"]},{"name":"📊 Usage Billing","tags":["Meters","Usage Events","Usage"]},{"name":"📦 Products & Pricing","tags":["Products","Prices","Discounts"]},{"name":"👥 Customers & Vault","tags":["Customers","Entitlements","Payment Methods"]},{"name":"💰 Money Movement","tags":["Transactions","Disputes","Payouts"]},{"name":"🔔 Webhooks & Events","tags":["Events","Webhook Endpoints"]},{"name":"🖥️ Terminal / Card Present","tags":["Terminal Payments","Terminal Devices"]},{"name":"📈 Analytics & Finance","tags":["Analytics","Finance"]},{"name":"📥 Data Management","tags":["Imports","Exports"]},{"name":"🔧 Developer Tools","tags":["Payment Attempts","External Sync","Integrations","Sync State"]}],"x-scalar-environments":[{"name":"Production","color":"#10B981","description":"Use rk_live_* keys. Real transactions.","variables":{"baseUrl":"https://api.revkeen.com/v2","apiKeyPrefix":"rk_live_"}},{"name":"Staging","color":"#3B82F6","description":"Use rk_sandbox_* keys. No real charges.","variables":{"baseUrl":"https://staging-api.revkeen.com/v2","apiKeyPrefix":"rk_sandbox_"}},{"name":"Mock","color":"#8B5CF6","description":"Hosted Scalar mock server for schema validation. No API key required.","variables":{"baseUrl":"https://mock-api.revkeen.com/v2","apiKeyPrefix":"mock_"}}],"servers":[{"url":"https://api.revkeen.com/v2","description":"Production"},{"url":"https://staging-api.revkeen.com/v2","description":"Staging"},{"url":"https://mock-api.revkeen.com/v2","description":"Mock"}],"security":[{"apiKey":[]},{"oauth2":[]}],"webhooks":{"payment.succeeded":{"post":{"operationId":"webhooks_payment_succeeded","summary":"Payment succeeded","description":"Sent when a payment is successfully captured. **Action required:** Provision goods or services to your customer.","tags":["Webhooks"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEvent"},"example":{"id":"evt_1a2b3c4d5e6f","type":"payment.succeeded","created":1705689600,"livemode":true,"data":{"object":{"id":"pay_abc123","object":"payment","amount":2500,"currency":"usd","status":"succeeded","customer_id":"cus_xyz789","invoice_id":"inv_def456"}}}}}},"responses":{"200":{"description":"Webhook received successfully"}}}},"payment.failed":{"post":{"operationId":"webhooks_payment_failed","summary":"Payment failed","description":"Sent when a payment attempt fails. Contains failure reason and suggested next steps.","tags":["Webhooks"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEvent"},"example":{"id":"evt_2b3c4d5e6f7g","type":"payment.failed","created":1705689600,"livemode":true,"data":{"object":{"id":"pay_fail123","object":"payment","amount":5000,"currency":"usd","status":"failed","customer_id":"cus_xyz789","invoice_id":"inv_def456","failure_code":"card_declined","failure_message":"Your card was declined. Please try a different payment method."}}}}}},"responses":{"200":{"description":"Webhook received successfully"}}}},"invoice.created":{"post":{"operationId":"webhooks_invoice_created","summary":"Invoice created","description":"Sent when a new invoice is created, either manually or from a subscription renewal.","tags":["Webhooks"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEvent"},"example":{"id":"evt_3c4d5e6f7g8h","type":"invoice.created","created":1705689600,"livemode":true,"data":{"object":{"id":"inv_abc123","object":"invoice","customer_id":"cus_xyz789","invoice_number":"INV-2025-0042","total_minor":9900,"currency":"usd","status":"draft","due_date":"2025-02-19T00:00:00Z"}}}}}},"responses":{"200":{"description":"Webhook received successfully"}}}},"invoice.paid":{"post":{"operationId":"webhooks_invoice_paid","summary":"Invoice paid","description":"Sent when an invoice is fully paid. **Action required:** Fulfill the order or activate the subscription.","tags":["Webhooks"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEvent"},"example":{"id":"evt_4d5e6f7g8h9i","type":"invoice.paid","created":1789914600,"livemode":true,"data":{"object":{"id":"inv_abc123","object":"invoice","customer_id":"cus_xyz789","invoice_number":"INV-2026-0042","total_minor":9900,"currency":"gbp","status":"paid","due_date":"2026-10-19T00:00:00Z","paid_at":"2026-09-20T14:30:00Z"}}}}}},"responses":{"200":{"description":"Webhook received successfully"}}}},"invoice.overdue":{"post":{"operationId":"webhooks_invoice_overdue","summary":"Invoice overdue","description":"Sent when an invoice becomes overdue. Consider sending payment reminders.","tags":["Webhooks"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEvent"},"example":{"id":"evt_5e6f7g8h9i0j","type":"invoice.overdue","created":1705689600,"livemode":true,"data":{"object":{"id":"inv_abc123","object":"invoice","customer_id":"cus_xyz789","invoice_number":"INV-2025-0042","total_minor":9900,"currency":"usd","status":"open","due_date":"2025-01-15T00:00:00Z","days_overdue":5}}}}}},"responses":{"200":{"description":"Webhook received successfully"}}}},"subscription.created":{"post":{"operationId":"webhooks_subscription_created","summary":"Subscription created","description":"Sent when a new subscription is created (before activation).","tags":["Webhooks"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEvent"},"example":{"id":"evt_6f7g8h9i0j1k","type":"subscription.created","created":1705689600,"livemode":true,"data":{"object":{"id":"sub_abc123","object":"subscription","customer_id":"cus_xyz789","status":"pending","current_period_start":"2025-01-20T00:00:00Z","current_period_end":"2025-02-20T00:00:00Z","items":[{"price_id":"price_abc123","quantity":1}]}}}}}},"responses":{"200":{"description":"Webhook received successfully"}}}},"subscription.activated":{"post":{"operationId":"webhooks_subscription_activated","summary":"Subscription activated","description":"Sent when a subscription becomes active after successful payment. **Action required:** Grant access to your service.","tags":["Webhooks"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEvent"},"example":{"id":"evt_7g8h9i0j1k2l","type":"subscription.activated","created":1705689600,"livemode":true,"data":{"object":{"id":"sub_abc123","object":"subscription","customer_id":"cus_xyz789","status":"active","current_period_start":"2025-01-20T00:00:00Z","current_period_end":"2025-02-20T00:00:00Z","items":[{"price_id":"price_abc123","quantity":1}]}}}}}},"responses":{"200":{"description":"Webhook received successfully"}}}},"subscription.canceled":{"post":{"operationId":"webhooks_subscription_canceled","summary":"Subscription canceled","description":"Sent when a subscription is canceled. Access continues until the end of the billing period.","tags":["Webhooks"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEvent"},"example":{"id":"evt_8h9i0j1k2l3m","type":"subscription.canceled","created":1705689600,"livemode":true,"data":{"object":{"id":"sub_abc123","object":"subscription","customer_id":"cus_xyz789","status":"canceled","cancel_at_period_end":true,"current_period_end":"2025-02-20T00:00:00Z","canceled_at":"2025-01-20T14:30:00Z"}}}}}},"responses":{"200":{"description":"Webhook received successfully"}}}},"subscription.renewed":{"post":{"operationId":"webhooks_subscription_renewed","summary":"Subscription renewed","description":"Sent when a subscription successfully renews. A new invoice has been created and paid.","tags":["Webhooks"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEvent"},"example":{"id":"evt_9i0j1k2l3m4n","type":"subscription.renewed","created":1705689600,"livemode":true,"data":{"object":{"id":"sub_abc123","object":"subscription","customer_id":"cus_xyz789","status":"active","current_period_start":"2025-02-20T00:00:00Z","current_period_end":"2025-03-20T00:00:00Z","latest_invoice_id":"inv_renewal456"}}}}}},"responses":{"200":{"description":"Webhook received successfully"}}}},"subscription.paused":{"post":{"operationId":"webhooks_subscription_paused","summary":"Subscription paused","description":"Sent when a subscription is paused immediately: by you through the API or the dashboard, or by the customer in the portal. A pause scheduled for the end of the period is not announced when it takes effect. Renewals stop until it is resumed; nothing already issued is voided or recalled. `data.object` is the subscription: `id`, `subscription_id`, `customer_id`, `status` (`paused`), `previous_status` and `effective_date`. **Action required:** decide whether the customer keeps access while the subscription is paused. Fetch the subscription for its current state.","tags":["Webhooks"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEvent"},"example":{"id":"evt_b1k2l3m4n5o6","type":"subscription.paused","created":1705689600,"livemode":true,"data":{"object":{"id":"7f4b1a52-6a3e-4c1d-9d52-0c5e2d8a9b10","object":"subscription","subscription_id":"7f4b1a52-6a3e-4c1d-9d52-0c5e2d8a9b10","customer_id":"3d9e2c41-8b7a-4f60-a1c5-5e2b7d9f4a33","status":"paused","previous_status":"active","effective_date":"2025-01-20T14:30:00.000Z"}}}}}},"responses":{"200":{"description":"Webhook received successfully"}}}},"subscription.resumed":{"post":{"operationId":"webhooks_subscription_resumed","summary":"Subscription resumed","description":"Sent when a paused subscription is resumed through the API or the dashboard. A resume the customer makes in the portal is not announced. The periods that elapsed while it was paused are not billed: the next charge is the next billing date after the resume. `data.object` is the subscription: `id`, `subscription_id`, `customer_id`, `status` (`active`), `previous_status` and `effective_date`. Fetch the subscription for its current period and next billing date.","tags":["Webhooks"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEvent"},"example":{"id":"evt_c2l3m4n5o6p7","type":"subscription.resumed","created":1705689600,"livemode":true,"data":{"object":{"id":"7f4b1a52-6a3e-4c1d-9d52-0c5e2d8a9b10","object":"subscription","subscription_id":"7f4b1a52-6a3e-4c1d-9d52-0c5e2d8a9b10","customer_id":"3d9e2c41-8b7a-4f60-a1c5-5e2b7d9f4a33","status":"active","previous_status":"paused","effective_date":"2025-03-02T09:15:00.000Z"}}}}}},"responses":{"200":{"description":"Webhook received successfully"}}}},"subscription.plan_changed":{"post":{"operationId":"webhooks_subscription_plan_changed","summary":"Subscription plan changed","description":"Sent when a subscription moves to another price: through the API (`POST /v2/subscriptions/{id}/change-plan`) or the dashboard. Nothing is charged when the plan changes; the proration is added to, or credited on, the next renewal invoice. `data.object` carries `subscription_id`, `customer_id`, `status`, `previous_status`, `previous_plan_name`, `new_plan_name`, `amount`, `currency`, `effective_date` and `action_source`. For a change made through the API (`action_source` `api`), `amount` is the new renewal amount in minor units. Fetch the subscription for its current price and amount.","tags":["Webhooks"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEvent"},"example":{"id":"evt_d3m4n5o6p7q8","type":"subscription.plan_changed","created":1705689600,"livemode":true,"data":{"object":{"id":"7f4b1a52-6a3e-4c1d-9d52-0c5e2d8a9b10","object":"subscription","subscription_id":"7f4b1a52-6a3e-4c1d-9d52-0c5e2d8a9b10","customer_id":"3d9e2c41-8b7a-4f60-a1c5-5e2b7d9f4a33","status":"active","previous_status":"active","previous_plan_name":"Basic","new_plan_name":"Pro","amount":3000,"currency":"GBP","effective_date":"2025-01-20T14:30:00.000Z","action_source":"api"}}}}}},"responses":{"200":{"description":"Webhook received successfully"}}}},"customer.created":{"post":{"operationId":"webhooks_customer_created","summary":"Customer created","description":"Sent when a new customer is created.","tags":["Webhooks"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEvent"},"example":{"id":"evt_a0j1k2l3m4n5","type":"customer.created","created":1705689600,"livemode":true,"data":{"object":{"id":"cus_new123","object":"customer","email":"jane@example.com","name":"Jane Smith","phone":"+15551234567","created_at":"2025-01-20T14:30:00Z"}}}}}},"responses":{"200":{"description":"Webhook received successfully"}}}},"customer.updated":{"post":{"operationId":"webhooks_customer_updated","summary":"Customer updated","description":"Sent when customer information is updated.","tags":["Webhooks"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEvent"},"example":{"id":"evt_b1k2l3m4n5o6","type":"customer.updated","created":1705689600,"livemode":true,"data":{"object":{"id":"cus_xyz789","object":"customer","email":"jane.updated@example.com","name":"Jane Smith-Jones","phone":"+15551234567","updated_at":"2025-01-20T15:45:00Z"}}}}}},"responses":{"200":{"description":"Webhook received successfully"}}}},"refund.created":{"post":{"operationId":"webhooks_refund_created","summary":"Refund created","description":"Sent when a refund is initiated.","tags":["Webhooks"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEvent"},"example":{"id":"evt_c2l3m4n5o6p7","type":"refund.created","created":1705689600,"livemode":true,"data":{"object":{"id":"ref_abc123","object":"refund","amount":2500,"currency":"usd","status":"pending","payment_id":"660e8400-e29b-41d4-a716-446655440000","reason":"customer_request"}}}}}},"responses":{"200":{"description":"Webhook received successfully"}}}},"refund.succeeded":{"post":{"operationId":"webhooks_refund_succeeded","summary":"Refund succeeded","description":"Sent when a refund is successfully processed by the payment gateway.","tags":["Webhooks"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEvent"},"example":{"id":"evt_d3m4n5o6p7q8","type":"refund.succeeded","created":1705689600,"livemode":true,"data":{"object":{"id":"ref_abc123","object":"refund","amount":2500,"currency":"usd","status":"succeeded","payment_id":"660e8400-e29b-41d4-a716-446655440000","reason":"customer_request"}}}}}},"responses":{"200":{"description":"Webhook received successfully"}}}},"void.created":{"post":{"operationId":"webhooks_void_created","summary":"Void created","description":"Sent when a void is initiated for an unsettled transaction.","tags":["Webhooks"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEvent"},"example":{"id":"evt_e4n5o6p7q8r9","type":"void.created","created":1705689600,"livemode":true,"data":{"object":{"id":"void_abc123","object":"void","amount":5000,"currency":"usd","status":"pending","payment_id":"660e8400-e29b-41d4-a716-446655440000","reason":"duplicate_charge"}}}}}},"responses":{"200":{"description":"Webhook received successfully"}}}},"void.succeeded":{"post":{"operationId":"webhooks_void_succeeded","summary":"Void succeeded","description":"Sent when a void is successfully processed by the payment gateway. The original transaction has been canceled.","tags":["Webhooks"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEvent"},"example":{"id":"evt_f5o6p7q8r9s0","type":"void.succeeded","created":1705689600,"livemode":true,"data":{"object":{"id":"void_abc123","object":"void","amount":5000,"currency":"usd","status":"succeeded","payment_id":"660e8400-e29b-41d4-a716-446655440000","reason":"duplicate_charge"}}}}}},"responses":{"200":{"description":"Webhook received successfully"}}}},"void.failed":{"post":{"operationId":"webhooks_void_failed","summary":"Void failed","description":"Sent when a void fails (e.g., transaction already settled). Consider using a refund instead.","tags":["Webhooks"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEvent"},"example":{"id":"evt_g6p7q8r9s0t1","type":"void.failed","created":1705689600,"livemode":true,"data":{"object":{"id":"void_abc123","object":"void","amount":5000,"currency":"usd","status":"failed","payment_id":"660e8400-e29b-41d4-a716-446655440000","failure_reason":"Transaction already settled. Use a refund instead."}}}}}},"responses":{"200":{"description":"Webhook received successfully"}}}},"credit_note.created":{"post":{"operationId":"webhooks_credit_note_created","summary":"Credit note created","description":"Sent when a credit note is created for an invoice.","tags":["Webhooks"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEvent"},"example":{"id":"evt_h7q8r9s0t1u2","type":"credit_note.created","created":1705689600,"livemode":true,"data":{"object":{"id":"cn_abc123","object":"credit_note","invoice_id":"inv_abc123","customer_id":"cus_xyz789","amount_minor":2500,"currency":"usd","status":"draft","reason":"Billing error — incorrect quantity","reason_code":"billing_error"}}}}}},"responses":{"200":{"description":"Webhook received successfully"}}}},"credit_note.issued":{"post":{"operationId":"webhooks_credit_note_issued","summary":"Credit note issued","description":"Sent when a credit note is issued (finalized) and its PDF is generated.","tags":["Webhooks"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEvent"},"example":{"id":"evt_i8r9s0t1u2v3","type":"credit_note.issued","created":1705689600,"livemode":true,"data":{"object":{"id":"cn_abc123","object":"credit_note","credit_note_number":"CN-2025-0007","invoice_id":"inv_abc123","customer_id":"cus_xyz789","amount_minor":2500,"currency":"usd","status":"issued","reason_code":"billing_error","credit_method":"refund_to_payment_method","issued_at":"2025-01-20T14:30:00Z","pdf_url":"https://api.revkeen.com/v2/credit-notes/cn_abc123/pdf"}}}}}},"responses":{"200":{"description":"Webhook received successfully"}}}},"credit_note.voided":{"post":{"operationId":"webhooks_credit_note_voided","summary":"Credit note voided","description":"Sent when a credit note is voided (accounting reversal).","tags":["Webhooks"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEvent"},"example":{"id":"evt_j9s0t1u2v3w4","type":"credit_note.voided","created":1705689600,"livemode":true,"data":{"object":{"id":"cn_abc123","object":"credit_note","credit_note_number":"CN-2025-0007","invoice_id":"inv_abc123","amount_minor":2500,"currency":"usd","status":"void","voided_at":"2025-01-21T10:00:00Z"}}}}}},"responses":{"200":{"description":"Webhook received successfully"}}}},"checkout.session.completed":{"post":{"operationId":"webhooks_checkout_session_completed","summary":"Checkout session completed","description":"Sent when a checkout session is successfully completed. **Action required:** Fulfill the customer's order.","tags":["Webhooks"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEvent"},"example":{"id":"evt_k0t1u2v3w4x5","type":"checkout.session.completed","created":1705689600,"livemode":true,"data":{"object":{"id":"cs_abc123","object":"checkout_session","customer_id":"cus_xyz789","payment_id":"660e8400-e29b-41d4-a716-446655440000","invoice_id":"inv_def456","amount_total":9900,"currency":"usd","status":"completed","success_url":"https://example.com/success"}}}}}},"responses":{"200":{"description":"Webhook received successfully"}}}},"checkout.session.expired":{"post":{"operationId":"webhooks_checkout_session_expired","summary":"Checkout session expired","description":"Sent when an open or pending checkout session is expired with `POST /v2/checkout-sessions/{id}/expire`. A session that only reaches its expiry time without being paid does not send this event.","tags":["Webhooks"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEvent"},"example":{"id":"evt_l1u2v3w4x5y6","type":"checkout.session.expired","created":1705689600,"livemode":true,"data":{"object":{"id":"cs_expired456","object":"checkout_session","invoice_id":"inv_def456","amount_total":9900,"currency":"usd","status":"expired","expires_at":"2025-01-20T15:30:00Z"}}}}}},"responses":{"200":{"description":"Webhook received successfully"}}}},"order.created":{"post":{"operationId":"webhooks_order_created","summary":"Order created","description":"Sent when a new order is created.","tags":["Webhooks"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEvent"},"example":{"id":"evt_m2v3w4x5y6z7","type":"order.created","created":1705689600,"livemode":true,"data":{"object":{"id":"ord_abc123","object":"order","customer_id":"cus_xyz789","invoice_id":"inv_def456","status":"pending","items":[{"product_id":"prod_abc123","quantity":2,"unit_amount":2500}]}}}}}},"responses":{"200":{"description":"Webhook received successfully"}}}},"order.paid":{"post":{"operationId":"webhooks_order_paid","summary":"Order paid","description":"Sent when an order is fully paid. **Action required:** Begin fulfillment.","tags":["Webhooks"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEvent"},"example":{"id":"evt_n3w4x5y6z7a8","type":"order.paid","created":1705689600,"livemode":true,"data":{"object":{"id":"ord_abc123","object":"order","customer_id":"cus_xyz789","invoice_id":"inv_def456","status":"paid","paid_at":"2025-01-20T14:30:00Z"}}}}}},"responses":{"200":{"description":"Webhook received successfully"}}}},"terminal_payment.requested":{"post":{"operationId":"webhooks_terminal_payment_requested","summary":"Terminal payment requested","description":"Sent when a card-present payment is dispatched to a terminal device. The payment is in-progress and awaiting terminal response.","tags":["Webhooks"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEvent"},"example":{"id":"evt_o4x5y6z7a8b9","type":"terminal_payment.requested","created":1705689600,"livemode":true,"data":{"object":{"id":"tpay_abc123","object":"terminal_payment","amount":3500,"currency":"usd","status":"requested","device_id":"dev_xyz789","terminal_serial":"PAX-A920-001","invoice_id":"inv_def456"}}}}}},"responses":{"200":{"description":"Webhook received successfully"}}}},"terminal_payment.succeeded":{"post":{"operationId":"webhooks_terminal_payment_succeeded","summary":"Terminal payment succeeded","description":"Sent when a card-present payment is approved by the terminal. **Action required:** Provide goods or services to the customer.","tags":["Webhooks"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEvent"},"example":{"id":"evt_p5y6z7a8b9c0","type":"terminal_payment.succeeded","created":1705689600,"livemode":true,"data":{"object":{"id":"tpay_abc123","object":"terminal_payment","amount":3500,"currency":"usd","status":"succeeded","device_id":"dev_xyz789","terminal_serial":"PAX-A920-001","invoice_id":"inv_def456","card_brand":"visa","last_four":"4242","auth_code":"A12345"}}}}}},"responses":{"200":{"description":"Webhook received successfully"}}}},"terminal_payment.declined":{"post":{"operationId":"webhooks_terminal_payment_declined","summary":"Terminal payment declined","description":"Sent when a card-present payment is declined by the terminal or card issuer.","tags":["Webhooks"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEvent"},"example":{"id":"evt_q6z7a8b9c0d1","type":"terminal_payment.declined","created":1705689600,"livemode":true,"data":{"object":{"id":"tpay_dec456","object":"terminal_payment","amount":3500,"currency":"usd","status":"declined","device_id":"dev_xyz789","terminal_serial":"PAX-A920-001","failure_code":"insufficient_funds","failure_message":"Card declined — insufficient funds"}}}}}},"responses":{"200":{"description":"Webhook received successfully"}}}},"terminal_payment.cancelled":{"post":{"operationId":"webhooks_terminal_payment_cancelled","summary":"Terminal payment cancelled","description":"Sent when a card-present payment is cancelled by the merchant before terminal response.","tags":["Webhooks"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEvent"},"example":{"id":"evt_r7a8b9c0d1e2","type":"terminal_payment.cancelled","created":1705689600,"livemode":true,"data":{"object":{"id":"tpay_can789","object":"terminal_payment","amount":3500,"currency":"usd","status":"cancelled","device_id":"dev_xyz789","terminal_serial":"PAX-A920-001","cancelled_at":"2025-01-20T14:31:00Z"}}}}}},"responses":{"200":{"description":"Webhook received successfully"}}}},"terminal_payment.error":{"post":{"operationId":"webhooks_terminal_payment_error","summary":"Terminal payment error","description":"Sent when a card-present payment fails due to timeout, terminal error, or connection loss. Check `failure_reason` field: `timeout`, `terminal_error`, or `connection_lost`.","tags":["Webhooks"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEvent"},"example":{"id":"evt_s8b9c0d1e2f3","type":"terminal_payment.error","created":1705689600,"livemode":true,"data":{"object":{"id":"tpay_err012","object":"terminal_payment","amount":3500,"currency":"usd","status":"error","device_id":"dev_xyz789","terminal_serial":"PAX-A920-001","failure_reason":"timeout","failure_message":"Terminal did not respond within 60 seconds"}}}}}},"responses":{"200":{"description":"Webhook received successfully"}}}},"terminal_refund.succeeded":{"post":{"operationId":"webhooks_terminal_refund_succeeded","summary":"Terminal refund succeeded","description":"Sent when a card-present refund is approved by the terminal.","tags":["Webhooks"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEvent"},"example":{"id":"evt_t9c0d1e2f3g4","type":"terminal_refund.succeeded","created":1705689600,"livemode":true,"data":{"object":{"id":"tref_abc123","object":"terminal_refund","amount":1500,"currency":"usd","status":"succeeded","payment_id":"tpay_abc123","device_id":"dev_xyz789","terminal_serial":"PAX-A920-001"}}}}}},"responses":{"200":{"description":"Webhook received successfully"}}}},"terminal_void.succeeded":{"post":{"operationId":"webhooks_terminal_void_succeeded","summary":"Terminal void succeeded","description":"Sent when a card-present void is approved by the terminal. The original transaction has been reversed.","tags":["Webhooks"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEvent"},"example":{"id":"evt_u0d1e2f3g4h5","type":"terminal_void.succeeded","created":1705689600,"livemode":true,"data":{"object":{"id":"tvoid_abc123","object":"terminal_void","amount":3500,"currency":"usd","status":"succeeded","payment_id":"tpay_abc123","device_id":"dev_xyz789","terminal_serial":"PAX-A920-001"}}}}}},"responses":{"200":{"description":"Webhook received successfully"}}}},"order.fulfilled":{"post":{"operationId":"webhooks_order_fulfilled","summary":"Order fulfilled","description":"Sent when an order is marked as fulfilled.","tags":["Webhooks"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEvent"},"example":{"id":"evt_v1e2f3g4h5i6","type":"order.fulfilled","created":1705689600,"livemode":true,"data":{"object":{"id":"ord_abc123","object":"order","customer_id":"cus_xyz789","status":"fulfilled","fulfilled_at":"2025-01-21T10:00:00Z"}}}}}},"responses":{"200":{"description":"Webhook received successfully"}}}},"usage.threshold.reached":{"post":{"operationId":"webhooks_usage_threshold_reached","summary":"Usage threshold reached","description":"Sent when a customer's usage crosses a configured threshold percentage on a meter. Use this to alert customers approaching or exceeding their included allowance.","tags":["Webhooks"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEvent"},"example":{"id":"evt_1a2b3c4d5e6f","type":"usage.threshold.reached","created":1710518400,"livemode":true,"data":{"object":{"object":"usage","meter_id":"a1b2c3d4-e5f6-7890-abcd-ef1234567890","meter_name":"API Calls","customer_id":"cus_xyz789","subscription_id":"sub_abc123","threshold":80,"current_usage":8500,"included_quantity":10000,"estimated_amount_minor":0,"currency":"gbp"}}}}}},"responses":{"200":{"description":"Webhook received successfully"}}}},"usage.period_finalized":{"post":{"operationId":"webhooks_usage_period_finalized","summary":"Usage period finalized","description":"Sent when a billing period is finalized with final usage quantities. An invoice will be generated shortly after.","tags":["Webhooks"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEvent"},"example":{"id":"evt_w2f3g4h5i6j7","type":"usage.period_finalized","created":1710518400,"livemode":true,"data":{"object":{"object":"usage","usage_record_id":"urec_abc123","meter_id":"a1b2c3d4-e5f6-7890-abcd-ef1234567890","meter_name":"API Calls","customer_id":"cus_xyz789","subscription_id":"sub_abc123","period_start":"2025-02-01T00:00:00Z","period_end":"2025-03-01T00:00:00Z","total_usage":12500,"event_count":12500,"included_quantity":10000,"overage_quantity":2500,"calculated_amount_minor":2500,"currency":"gbp"}}}}}},"responses":{"200":{"description":"Webhook received successfully"}}}},"usage.invoice.created":{"post":{"operationId":"webhooks_usage_invoice_created","summary":"Usage invoice created","description":"Sent when finalized usage records are converted into an invoice for a customer subscription.","tags":["Webhooks"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEvent"},"example":{"id":"evt_y4h5i6j7k8l9","type":"usage.invoice.created","created":1711929601,"livemode":true,"data":{"object":{"object":"usage_invoice","invoice_id":"inv_usage_001","invoice_number":"INV-2026-0042","customer_id":"cus_xyz789","subscription_id":"sub_abc123","total_amount_minor":107000,"currency":"gbp","period_start":"2026-03-01T00:00:00Z","period_end":"2026-03-31T23:59:59Z","line_items":[{"meter_id":"a1b2c3d4-e5f6-7890-abcd-ef1234567890","meter_name":"API Calls","quantity":15000,"amount_minor":107000}]}}}}}},"responses":{"200":{"description":"Webhook received successfully"}}}},"usage.event.ingested":{"post":{"operationId":"webhooks_usage_event_ingested","summary":"Usage event ingested","description":"Sent after a usage event is accepted by ingestion. This is a high-volume event and should be subscribed to explicitly with narrow endpoint filters.","tags":["Webhooks"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEvent"},"example":{"id":"evt_z5i6j7k8l9m0","type":"usage.event.ingested","created":1710518460,"livemode":true,"data":{"object":{"object":"usage_event","event_id":"usage_evt_abc123","idempotency_key":"api_call_2026_03_15_001","event_name":"api_call","meter_id":"a1b2c3d4-e5f6-7890-abcd-ef1234567890","meter_name":"API Calls","customer_id":"cus_xyz789","external_customer_id":"customer_123","subscription_id":"sub_abc123","quantity":1,"billable_quantity":1,"event_time":"2026-03-15T14:30:58Z","source":"api"}}}}}},"responses":{"200":{"description":"Webhook received successfully"}}}},"usage.event.rejected":{"post":{"operationId":"webhooks_usage_event_rejected","summary":"Usage event rejected","description":"Sent when a usage event is rejected during ingestion validation. Rejected events are not billable.","tags":["Webhooks"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEvent"},"example":{"id":"evt_x3g4h5i6j7k8","type":"usage.event.rejected","created":1710518400,"livemode":true,"data":{"object":{"object":"usage_event","meter_id":"a1b2c3d4-e5f6-7890-abcd-ef1234567890","customer_id":"cus_xyz789","event_id":"usage_evt_abc123","event_name":"api_call","rejection_reason":"validation_failed","rejection_message":"Property 'quantity' must be a positive number","source":"api"}}}}}},"responses":{"200":{"description":"Webhook received successfully"}}}},"usage.event.excluded":{"post":{"operationId":"webhooks_usage_event_excluded","summary":"Usage event excluded","description":"Sent when a usage event is considered by a meter but excluded by filter conditions. The event is not billable. Delivery is at-least-once; dedupe on `data.dedupeKey`.","tags":["Webhooks"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEvent"},"example":{"id":"evt_usage_excluded_001","type":"usage.event.excluded","created":1710518460,"livemode":true,"data":{"object":{"object":"usage_event","event_id":"usage_evt_abc123","meter_id":"a1b2c3d4-e5f6-7890-abcd-ef1234567890","event_name":"api_call","quantity":1,"reason":"did not match meter filter","exclusion_reason":"did not match meter filter","dedupe_key":"usage:usage_evt_abc123:excluded"}}}}}},"responses":{"200":{"description":"Webhook received successfully"}}}},"usage.event.quarantined":{"post":{"operationId":"webhooks_usage_event_quarantined","summary":"Usage event quarantined","description":"Sent when a usage event cannot be matched to a single active meter (unmatched, ambiguous, or terminal processing failure). Delivery is at-least-once; dedupe on `data.dedupeKey`.","tags":["Webhooks"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEvent"},"example":{"id":"evt_usage_quarantined_001","type":"usage.event.quarantined","created":1710518460,"livemode":true,"data":{"object":{"object":"usage_event","event_id":"usage_evt_abc123","event_name":"api_call","quantity":1,"reason":"no active meter for event_name","dedupe_key":"usage:usage_evt_abc123:quarantined"}}}}}},"responses":{"200":{"description":"Webhook received successfully"}}}},"usage.cap.exceeded":{"post":{"operationId":"webhooks_usage_cap_exceeded","summary":"Usage cap exceeded","description":"Sent when a hard usage cap refuses the event. The ingest transaction rolled back: nothing was persisted and nothing was charged. Delivery is at-least-once; dedupe on `data.dedupeKey`.","tags":["Webhooks"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEvent"},"example":{"id":"evt_usage_cap_001","type":"usage.cap.exceeded","created":1710518460,"livemode":true,"data":{"object":{"object":"usage","meter_id":"a1b2c3d4-e5f6-7890-abcd-ef1234567890","event_name":"api_call","quantity":1,"reason":"hard_cap","cap":"10000","running_quantity":"10000","dedupe_key":"usage:batch:0:cap_exceeded"}}}}}},"responses":{"200":{"description":"Webhook received successfully"}}}},"meter.created":{"post":{"operationId":"webhooks_meter_created","summary":"Meter created","description":"Sent when a usage meter is created.","tags":["Webhooks"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEvent"},"example":{"id":"evt_meter_created_001","type":"meter.created","created":1710061200,"livemode":true,"data":{"object":{"object":"meter","id":"mtr_abc123","name":"API Calls","event_name":"api_call","aggregation":"sum","unit_name":"call","status":"active","value_key":"quantity"}}}}}},"responses":{"200":{"description":"Webhook received successfully"}}}},"meter.updated":{"post":{"operationId":"webhooks_meter_updated","summary":"Meter updated","description":"Sent when a usage meter is updated. The payload includes changed attributes when available.","tags":["Webhooks"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEvent"},"example":{"id":"evt_meter_updated_001","type":"meter.updated","created":1710064800,"livemode":true,"data":{"object":{"object":"meter","id":"mtr_abc123","name":"API Requests","event_name":"api_call","aggregation":"sum","unit_name":"request","status":"active","value_key":"quantity"},"previous_attributes":{"name":"API Calls","unit_name":"call"}}}}}},"responses":{"200":{"description":"Webhook received successfully"}}}},"meter.archived":{"post":{"operationId":"webhooks_meter_archived","summary":"Meter archived","description":"Sent when a usage meter is archived and can no longer be attached to new pricing.","tags":["Webhooks"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEvent"},"example":{"id":"evt_meter_archived_001","type":"meter.archived","created":1710151200,"livemode":true,"data":{"object":{"object":"meter","id":"mtr_abc123","name":"API Requests","event_name":"api_call","aggregation":"sum","unit_name":"request","status":"archived","value_key":"quantity","archived_at":"2026-03-11T10:00:00Z"}}}}}},"responses":{"200":{"description":"Webhook received successfully"}}}},"meter_price.created":{"post":{"operationId":"webhooks_meter_price_created","summary":"Meter price created","description":"Sent when a meter price is created for a product price.","tags":["Webhooks"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEvent"},"example":{"id":"evt_meter_price_created_001","type":"meter_price.created","created":1710062100,"livemode":true,"data":{"object":{"object":"meter_price","id":"mp_abc123","meter_id":"mtr_abc123","name":"API Calls overage","pricing_model":"tiered","billing_scheme":"per_unit","tiers_mode":"graduated","currency":"gbp","unit_amount_minor":null,"active":true}}}}}},"responses":{"200":{"description":"Webhook received successfully"}}}},"meter_price.updated":{"post":{"operationId":"webhooks_meter_price_updated","summary":"Meter price updated","description":"Sent when a meter price is updated. The payload includes changed attributes when available.","tags":["Webhooks"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEvent"},"example":{"id":"evt_meter_price_updated_001","type":"meter_price.updated","created":1710065700,"livemode":true,"data":{"object":{"object":"meter_price","id":"mp_abc123","meter_id":"mtr_abc123","name":"API Requests overage","pricing_model":"tiered","billing_scheme":"per_unit","tiers_mode":"graduated","currency":"gbp","unit_amount_minor":null,"active":true},"previous_attributes":{"name":"API Calls overage"}}}}}},"responses":{"200":{"description":"Webhook received successfully"}}}},"meter_price.deactivated":{"post":{"operationId":"webhooks_meter_price_deactivated","summary":"Meter price deactivated","description":"Sent when a meter price is deactivated and no longer available for new subscriptions.","tags":["Webhooks"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEvent"},"example":{"id":"evt_meter_price_deactivated_001","type":"meter_price.deactivated","created":1710152100,"livemode":true,"data":{"object":{"object":"meter_price","id":"mp_abc123","meter_id":"mtr_abc123","name":"API Requests overage","pricing_model":"tiered","billing_scheme":"per_unit","tiers_mode":"graduated","currency":"gbp","unit_amount_minor":null,"active":false,"deactivated_at":"2026-03-11T10:15:00Z"}}}}}},"responses":{"200":{"description":"Webhook received successfully"}}}},"mandate.signed":{"post":{"operationId":"webhooks_mandate_signed","summary":"Mandate signed","description":"Sent when a customer signs a Direct Debit mandate instruction. The mandate is pending lodgement with the bank (AUDDIS) — wait for `mandate.activated` before collecting.","tags":["Webhooks"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEvent"},"example":{"id":"evt_0m1a2n3d4a5t","type":"mandate.signed","created":1705689600,"livemode":true,"data":{"object":{"id":"man_8a7b6c5d4e3f","object":"mandate","mandate_reference":"RK-A1B2C3","customer_id":"cus_xyz789","status":"pending_lodgement","bank_name":"Barclays","account_number_last4":"1234"}}}}}},"responses":{"200":{"description":"Webhook received successfully"}}}},"mandate.activated":{"post":{"operationId":"webhooks_mandate_activated","summary":"Mandate activated","description":"Sent when a mandate is lodged and active. **Action required:** You can now collect from this customer via Direct Debit.","tags":["Webhooks"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEvent"},"example":{"id":"evt_2m3a4n5d6a7t","type":"mandate.activated","created":1705862400,"livemode":true,"data":{"object":{"id":"man_8a7b6c5d4e3f","object":"mandate","mandate_reference":"RK-A1B2C3","customer_id":"cus_xyz789","status":"active","bank_name":"Barclays","account_number_last4":"1234"}}}}}},"responses":{"200":{"description":"Webhook received successfully"}}}},"mandate.suspended":{"post":{"operationId":"webhooks_mandate_suspended","summary":"Mandate suspended","description":"Sent when a mandate is suspended and can no longer be collected against until reinstated. Also sent when the customer's bank rejects a new mandate's setup (AUDDIS); the failure reason names the rejection. **Action required** in that case: re-collect bank details or use another payment method.","tags":["Webhooks"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEvent"},"example":{"id":"evt_4m5a6n7d8a9t","type":"mandate.suspended","created":1705948800,"livemode":true,"data":{"object":{"id":"man_8a7b6c5d4e3f","object":"mandate","mandate_reference":"RK-A1B2C3","customer_id":"cus_xyz789","status":"suspended"}}}}}},"responses":{"200":{"description":"Webhook received successfully"}}}},"mandate.cancelled":{"post":{"operationId":"webhooks_mandate_cancelled","summary":"Mandate cancelled","description":"Sent when a mandate is cancelled (by you, the customer, or the bank). It can no longer be collected against.","tags":["Webhooks"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEvent"},"example":{"id":"evt_5m6a7n8d9a0t","type":"mandate.cancelled","created":1705948800,"livemode":true,"data":{"object":{"id":"man_8a7b6c5d4e3f","object":"mandate","mandate_reference":"RK-A1B2C3","customer_id":"cus_xyz789","status":"cancelled"}}}}}},"responses":{"200":{"description":"Webhook received successfully"}}}},"collection.scheduled":{"post":{"operationId":"webhooks_collection_scheduled","summary":"Collection scheduled","description":"Sent when a Direct Debit collection is scheduled against an active mandate. Includes the working-day collection date.","tags":["Webhooks"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEvent"},"example":{"id":"evt_1c2o3l4l5e6c","type":"collection.scheduled","created":1705689600,"livemode":true,"data":{"object":{"id":"col_1a2b3c4d5e6f","object":"collection","amount":2500,"currency":"gbp","status":"scheduled","collection_date":"2026-07-01","mandate_id":"man_8a7b6c5d4e3f","invoice_id":"inv_def456"}}}}}},"responses":{"200":{"description":"Webhook received successfully"}}}},"collection.notice_sent":{"post":{"operationId":"webhooks_collection_notice_sent","summary":"Advance notice sent","description":"Sent when the mandatory advance notice for an upcoming collection has been issued to the customer.","tags":["Webhooks"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEvent"},"example":{"id":"evt_2c3o4l5l6e7c","type":"collection.notice_sent","created":1705689600,"livemode":true,"data":{"object":{"id":"col_1a2b3c4d5e6f","object":"collection","amount":2500,"currency":"gbp","status":"scheduled","collection_date":"2026-07-01","notice_date":"2026-06-20","mandate_id":"man_8a7b6c5d4e3f"}}}}}},"responses":{"200":{"description":"Webhook received successfully"}}}},"collection.succeeded":{"post":{"operationId":"webhooks_collection_succeeded","summary":"Collection succeeded","description":"Sent when a Direct Debit collection is successfully taken. **Action required:** Provision goods or services to your customer.","tags":["Webhooks"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEvent"},"example":{"id":"evt_3c4o5l6l7e8c","type":"collection.succeeded","created":1706140800,"livemode":true,"data":{"object":{"id":"col_1a2b3c4d5e6f","object":"collection","amount":2500,"currency":"gbp","status":"succeeded","collection_date":"2026-07-01","mandate_id":"man_8a7b6c5d4e3f","invoice_id":"inv_def456"}}}}}},"responses":{"200":{"description":"Webhook received successfully"}}}},"collection.failed":{"post":{"operationId":"webhooks_collection_failed","summary":"Collection failed","description":"Sent when a Direct Debit collection fails (e.g. insufficient funds). Contains the Bacs reason code. **Action required:** RevKeen Recovery may retry or fall back to a stored card per your policy.","tags":["Webhooks"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEvent"},"example":{"id":"evt_4c5o6l7l8e9c","type":"collection.failed","created":1706140800,"livemode":true,"data":{"object":{"id":"col_1a2b3c4d5e6f","object":"collection","amount":2500,"currency":"gbp","status":"failed","collection_date":"2026-07-01","mandate_id":"man_8a7b6c5d4e3f","invoice_id":"inv_def456","failure_code":"insufficient_funds","failure_message":"The collection was returned by the customer's bank."}}}}}},"responses":{"200":{"description":"Webhook received successfully"}}}},"collection.indemnity_claimed":{"post":{"operationId":"webhooks_collection_indemnity_claimed","summary":"Indemnity claimed","description":"Sent when a customer raises a Direct Debit indemnity claim (the Bacs chargeback equivalent) and the collection is reversed. **Action required:** Review the disputed collection.","tags":["Webhooks"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEvent"},"example":{"id":"evt_5c6o7l8l9e0c","type":"collection.indemnity_claimed","created":1706227200,"livemode":true,"data":{"object":{"id":"col_1a2b3c4d5e6f","object":"collection","amount":2500,"currency":"gbp","status":"indemnity_claimed","mandate_id":"man_8a7b6c5d4e3f","invoice_id":"inv_def456"}}}}}},"responses":{"200":{"description":"Webhook received successfully"}}}},"settlement.created":{"post":{"operationId":"webhooks_settlement_created","summary":"Settlement created","description":"Sent when collected Direct Debit funds are settled and a payout is created. Gross settlement — the bureau fee is reported separately and never netted from the payout.","tags":["Webhooks"],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEvent"},"example":{"id":"evt_1s2e3t4t5l6e","type":"settlement.created","created":1706313600,"livemode":true,"data":{"object":{"id":"stl_9z8y7x6w5v4u","object":"settlement","amount":2500,"currency":"gbp","settlement_date":"2026-07-03","payout_id":"po_abc123"}}}}}},"responses":{"200":{"description":"Webhook received successfully"}}}}},"components":{"securitySchemes":{"apiKey":{"type":"apiKey","in":"header","name":"x-api-key","description":"Your RevKeen merchant API key. Create and manage keys in [Dashboard → Settings → Developer](https://app.revkeen.com/dashboard/settings/general?tab=developer). Use `rk_sandbox_*` for staging/test and `rk_live_*` for production. The same key may be sent as `Authorization: Bearer <key>` if that suits your HTTP client better. A missing, invalid, expired, or revoked key returns `401` with a `WWW-Authenticate: Bearer` challenge; a valid key without the required scope returns `403`."},"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Session token (internal dashboard use only)"},"oauth2":{"type":"oauth2","description":"OAuth 2.1 with PKCE for MCP clients and third-party integrations","flows":{"authorizationCode":{"authorizationUrl":"https://app.revkeen.com/api/auth/oauth2/authorize","tokenUrl":"https://api.revkeen.com/api/auth/oauth2/token","refreshUrl":"https://api.revkeen.com/api/auth/oauth2/token","scopes":{"checkout:read":"View checkout session details","checkout:write":"Create and manage checkout sessions","cart:read":"View cart session details","cart:write":"Create and mutate cart sessions, line items, add-ons","payment_links:read":"View payment links","payment_links:write":"Create and manage payment links","charges:read":"View one-time charges","charges:write":"Create one-time charges for customers","payments:read":"View payment details","payments:write":"Capture or void payments","payment_intents:read":"View payment intent details","payment_intents:write":"Create, confirm, capture, and cancel payment intents","setup_intents:read":"View setup intent details","setup_intents:write":"Create, confirm, and cancel setup intents","payment_methods:read":"View saved payment methods","payment_methods:write":"Attach and detach payment methods","invoices:read":"View invoices","invoices:write":"Create, update, and manage invoices","subscriptions:read":"View subscriptions","subscriptions:write":"Create, update, pause, and cancel subscriptions","subscription_schedules:read":"View subscription schedule details","subscription_schedules:write":"Create, update, cancel, and release subscription schedules","orders:read":"View orders","orders:write":"Create and manage orders","credit_notes:read":"View credit notes","credit_notes:write":"Create and void credit notes","products:read":"View product catalog","products:write":"Create and update products","prices:read":"View pricing information","prices:write":"Create and update prices","discounts:read":"View discount codes","discounts:write":"Create and manage discount codes","meters:read":"View meter configurations","meters:write":"Create and update meters","usage:read":"View usage events and balances","usage:write":"Ingest usage events","customers:read":"View customer information","customers:write":"Create and update customers","entitlements:read":"View customer entitlements / feature access","entitlements:write":"Grant and revoke customer entitlements","businesses:read":"View business entities","businesses:write":"Manage business entities","refunds:read":"View refund details","refunds:write":"Issue refunds","voids:read":"View voided transactions","voids:write":"Void unsettled transactions","disputes:read":"View chargebacks and disputes","payouts:read":"View payout and settlement data","mandates:read":"View Direct Debit mandates and collection status","mandates:write":"Create, suspend, reinstate, and cancel Direct Debit mandates","terminal:read":"View terminal devices and card-present payments","terminal:write":"Initiate, cancel, refund, and void terminal payments","exports:read":"View and download data exports","exports:write":"Create data exports","imports:read":"View import status and history","imports:write":"Upload and run data imports","analytics:read":"View analytics and reports","finance:read":"View financial reports","comms:read":"View SMS and email delivery logs","comms:write":"Send SMS, email, and WhatsApp messages","automations:read":"View automations, runs, approvals, and traces","automations:write":"Create automations and trigger runs","apps:read":"View connected applications","apps:write":"Manage app connections","webhooks:read":"View webhook endpoints","webhooks:write":"Manage webhook endpoints","integrations:read":"View integration status and sync logs","integrations:write":"Activate, configure, and sync integrations","events:read":"View webhook event logs","events:write":"Resend and test webhook events","sync:read":"View sync watermarks and state","sync:write":"Update sync watermarks"}},"clientCredentials":{"tokenUrl":"https://api.revkeen.com/api/auth/oauth2/token","scopes":{"checkout:read":"View checkout session details","checkout:write":"Create and manage checkout sessions","cart:read":"View cart session details","cart:write":"Create and mutate cart sessions, line items, add-ons","payment_links:read":"View payment links","payment_links:write":"Create and manage payment links","charges:read":"View one-time charges","charges:write":"Create one-time charges for customers","payments:read":"View payment details","payments:write":"Capture or void payments","payment_intents:read":"View payment intent details","payment_intents:write":"Create, confirm, capture, and cancel payment intents","setup_intents:read":"View setup intent details","setup_intents:write":"Create, confirm, and cancel setup intents","payment_methods:read":"View saved payment methods","payment_methods:write":"Attach and detach payment methods","invoices:read":"View invoices","invoices:write":"Create, update, and manage invoices","subscriptions:read":"View subscriptions","subscriptions:write":"Create, update, pause, and cancel subscriptions","subscription_schedules:read":"View subscription schedule details","subscription_schedules:write":"Create, update, cancel, and release subscription schedules","orders:read":"View orders","orders:write":"Create and manage orders","credit_notes:read":"View credit notes","credit_notes:write":"Create and void credit notes","products:read":"View product catalog","products:write":"Create and update products","prices:read":"View pricing information","prices:write":"Create and update prices","discounts:read":"View discount codes","discounts:write":"Create and manage discount codes","meters:read":"View meter configurations","meters:write":"Create and update meters","usage:read":"View usage events and balances","usage:write":"Ingest usage events","customers:read":"View customer information","customers:write":"Create and update customers","entitlements:read":"View customer entitlements / feature access","entitlements:write":"Grant and revoke customer entitlements","businesses:read":"View business entities","businesses:write":"Manage business entities","refunds:read":"View refund details","refunds:write":"Issue refunds","voids:read":"View voided transactions","voids:write":"Void unsettled transactions","disputes:read":"View chargebacks and disputes","payouts:read":"View payout and settlement data","mandates:read":"View Direct Debit mandates and collection status","mandates:write":"Create, suspend, reinstate, and cancel Direct Debit mandates","terminal:read":"View terminal devices and card-present payments","terminal:write":"Initiate, cancel, refund, and void terminal payments","exports:read":"View and download data exports","exports:write":"Create data exports","imports:read":"View import status and history","imports:write":"Upload and run data imports","analytics:read":"View analytics and reports","finance:read":"View financial reports","comms:read":"View SMS and email delivery logs","comms:write":"Send SMS, email, and WhatsApp messages","automations:read":"View automations, runs, approvals, and traces","automations:write":"Create automations and trigger runs","apps:read":"View connected applications","apps:write":"Manage app connections","webhooks:read":"View webhook endpoints","webhooks:write":"Manage webhook endpoints","integrations:read":"View integration status and sync logs","integrations:write":"Activate, configure, and sync integrations","events:read":"View webhook event logs","events:write":"Resend and test webhook events","sync:read":"View sync watermarks and state","sync:write":"Update sync watermarks"}}}}},"schemas":{"WebhookEvent":{"type":"object","description":"The envelope for all webhook event deliveries. Contains the event type, timestamp, and the affected object in the data field.","required":["id","type","created","data"],"properties":{"id":{"type":"string","description":"Unique event identifier","example":"evt_1a2b3c4d5e6f"},"type":{"type":"string","description":"Event type using dot notation (e.g., `payment.succeeded`)","example":"payment.succeeded"},"created":{"type":"integer","description":"Unix timestamp when the event was created","example":1705689600},"livemode":{"type":"boolean","description":"Whether this event occurred in live mode","example":true},"data":{"type":"object","description":"Event payload containing the affected object","properties":{"object":{"type":"object","description":"The object that triggered the event (e.g., Payment, Invoice, Subscription)"},"previous_attributes":{"type":"object","description":"Previous values of attributes that changed (for update events)"}}}}},"AuthenticationError":{"type":"object","properties":{"error":{"type":"object","properties":{"type":{"type":"string","enum":["authentication_error"]},"code":{"type":"string","enum":["authentication_failed","invalid_api_key","expired_api_key","api_key_revoked","session_invalid","merchant_required"]},"message":{"type":"string"},"request_id":{"type":"string"}},"required":["type","code","message"]}},"required":["error"],"description":"Returned when the API key is missing, invalid, expired, or revoked. Verify your x-api-key header contains a valid key. A WWW-Authenticate: Bearer challenge header accompanies every 401."},"ForbiddenError":{"type":"object","properties":{"error":{"type":"object","properties":{"type":{"type":"string","enum":["authorization_error"]},"code":{"type":"string","enum":["insufficient_permissions","forbidden","ip_not_allowed","tenant_scope_mismatch","no_organization","no_merchant","merchant_mismatch","cart_disabled","origin_not_allowed","verification_required","read_only_impersonation_blocked","publishable_key_restricted","internal_only"]},"message":{"type":"string"},"request_id":{"type":"string"},"details":{"type":"object","additionalProperties":true,"description":"Additional context (e.g. required scopes)"}},"required":["type","code","message"]}},"required":["error"],"description":"Returned when the authenticated caller lacks the required scopes or the operation is not permitted for this key, origin, or merchant state."},"SubscriptionTerms":{"type":"object","properties":{"collection_method":{"type":"string","enum":["charge_automatically","send_invoice"],"description":"How to collect payment for recurring invoices","example":"charge_automatically"},"start_mode":{"type":"string","enum":["when_paid","on_invoice_date","first_of_next_month","specific_date"],"description":"When the subscription billing cycle starts","example":"when_paid"},"start_date":{"type":"string","description":"Start date (ISO 8601) — required when start_mode is 'specific_date'"},"duration_type":{"type":"string","enum":["until_cancelled","fixed_cycles","end_date"],"description":"How the subscription duration is determined","example":"until_cancelled"},"duration_count":{"type":"integer","minimum":1,"description":"Number of billing cycles — required when duration_type is 'fixed_cycles'","example":12},"end_date":{"type":"string","description":"End date (ISO 8601) — required when duration_type is 'end_date'"},"first_payment_behavior":{"type":"string","enum":["charge_first_cycle_now","defer_first_cycle_to_start_date"],"description":"Whether to charge the first cycle immediately or defer to the start date","example":"charge_first_cycle_now"}},"required":["collection_method","start_mode","duration_type","first_payment_behavior"],"description":"Subscription billing terms for recurring invoices"},"InvoiceUsageExplainResponse":{"type":"object","properties":{"invoice":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"customer_id":{"type":["string","null"],"format":"uuid"},"status":{"type":["string","null"]},"amount_due_minor":{"type":["integer","null"]},"currency":{"type":["string","null"]}},"required":["id","customer_id","status","amount_due_minor","currency"]},"lines":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"description":{"type":["string","null"]},"kind":{"type":"string","enum":["usage","recurring","one_time"]},"amount_minor":{"type":["integer","null"]},"currency":{"type":["string","null"]},"meter_id":{"type":["string","null"],"format":"uuid"},"usage_quantity":{"type":["string","null"]},"usage_event_count":{"type":["integer","null"]}},"required":["id","description","kind","amount_minor","currency","meter_id","usage_quantity","usage_event_count"]}},"summary":{"type":"string"}},"required":["invoice","lines","summary"],"description":"Invoice usage explanation: totals, line attribution, and usage event counts."},"InvoiceListResponse":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Invoice"}},"requestId":{"type":"string"}},"required":["data","requestId"],"description":"A list of invoices matching the query filters.","example":{"data":[{"id":"f1a2b3c4-d5e6-4270-8a9b-0c1d2e3f4a21","customer_uuid":"c2a1e7d4-5b3f-4a8e-9d21-6f0b3e4c7a11","invoice_number":"INV-0042","total_minor":5880,"currency":"GBP","allowed_methods":["card"],"status":"paid","due_date":"2026-04-16T10:00:00.000Z","external_source":null,"external_type":null,"external_id":null,"hosted_invoice_url":"https://pay.revkeen.com/inv_example_0042","created_at":"2026-04-16T10:00:00.000Z","updated_at":"2026-04-16T10:02:00.000Z"}],"requestId":"req_7f3a9c2e5b1d"}},"Invoice":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Unique invoice identifier"},"customer_uuid":{"type":["string","null"],"format":"uuid","description":"ID of the customer this invoice belongs to. `null` when no customer is recorded on the invoice."},"invoice_number":{"type":"string","description":"Merchant-assigned invoice number for reference"},"total_minor":{"type":"integer","description":"Total amount in minor units (cents)"},"currency":{"type":"string","description":"Three-letter ISO 4217 currency code"},"allowed_methods":{"type":["array","null"],"items":{"type":"string"},"description":"Allowed payment methods for checkout sessions against this invoice"},"status":{"type":"string","description":"Invoice lifecycle status: draft, open, finalized, paid, void, or uncollectible"},"due_date":{"type":["string","null"],"description":"Payment due date in ISO 8601 format"},"custom_fields":{"type":"object","additionalProperties":true,"description":"Key-value pairs for custom fields"},"external_source":{"type":["string","null"],"description":"External integration source (e.g., practicehub, wodify)","example":"practicehub"},"external_type":{"type":["string","null"],"description":"Type within external system (e.g., appointment, membership)","example":"appointment"},"external_id":{"type":["string","null"],"description":"ID from external system","example":"INV-12345"},"subscription_terms":{"type":["object","null"],"properties":{"collection_method":{"type":"string","enum":["charge_automatically","send_invoice"],"description":"How to collect payment for recurring invoices","example":"charge_automatically"},"start_mode":{"type":"string","enum":["when_paid","on_invoice_date","first_of_next_month","specific_date"],"description":"When the subscription billing cycle starts","example":"when_paid"},"start_date":{"type":"string","description":"Start date (ISO 8601) — required when start_mode is 'specific_date'"},"duration_type":{"type":"string","enum":["until_cancelled","fixed_cycles","end_date"],"description":"How the subscription duration is determined","example":"until_cancelled"},"duration_count":{"type":"integer","minimum":1,"description":"Number of billing cycles — required when duration_type is 'fixed_cycles'","example":12},"end_date":{"type":"string","description":"End date (ISO 8601) — required when duration_type is 'end_date'"},"first_payment_behavior":{"type":"string","enum":["charge_first_cycle_now","defer_first_cycle_to_start_date"],"description":"Whether to charge the first cycle immediately or defer to the start date","example":"charge_first_cycle_now"}},"required":["collection_method","start_mode","duration_type","first_payment_behavior"],"description":"Subscription billing terms for recurring invoices"},"hosted_invoice_url":{"type":["string","null"],"description":"URL of the hosted invoice page where the customer can view and pay this invoice. Null for invoices that have not been finalized (draft, pending approval, approved, imported). Treat it as a secret link: anyone who holds it can view and pay the invoice.","example":"https://pay.revkeen.com/inv_0b8f3c1e-5d2a-4f7b-9c61-2e4a7d9b1f03"},"created_at":{"type":"string","format":"date-time","description":"When the invoice was created (ISO 8601)"},"updated_at":{"type":"string","format":"date-time","description":"When the invoice was last updated (ISO 8601)"}},"required":["id","customer_uuid","total_minor","currency","status","hosted_invoice_url","created_at","updated_at"],"description":"The canonical record of what a customer owes or has paid. Invoices track line items, amounts, and payment status through their lifecycle: draft → open → finalized → paid.","example":{"id":"f1a2b3c4-d5e6-4270-8a9b-0c1d2e3f4a21","customer_uuid":"c2a1e7d4-5b3f-4a8e-9d21-6f0b3e4c7a11","invoice_number":"INV-0042","total_minor":5880,"currency":"GBP","allowed_methods":["card"],"status":"paid","due_date":"2026-04-16T10:00:00.000Z","external_source":null,"external_type":null,"external_id":null,"hosted_invoice_url":"https://pay.revkeen.com/inv_example_0042","created_at":"2026-04-16T10:00:00.000Z","updated_at":"2026-04-16T10:02:00.000Z"}},"InvoiceListValidationError":{"type":"object","properties":{"error":{"type":"object","properties":{"type":{"type":"string","enum":["invalid_request_error"]},"code":{"type":"string","enum":["parameter_invalid"]},"message":{"type":"string"},"request_id":{"type":"string"},"param":{"type":"string","enum":["status","customerId","limit","offset"],"description":"The query parameter that was rejected."}},"required":["type","code","message","param"]}},"required":["error"],"description":"Returned when a query parameter fails validation. `param` names the parameter to fix."},"InvoiceResponse":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Invoice"},"requestId":{"type":"string"}},"required":["data","requestId"],"description":"A single invoice object with request tracking metadata.","example":{"data":{"id":"f1a2b3c4-d5e6-4270-8a9b-0c1d2e3f4a21","customer_uuid":"c2a1e7d4-5b3f-4a8e-9d21-6f0b3e4c7a11","invoice_number":"INV-0042","total_minor":5880,"currency":"GBP","allowed_methods":["card"],"status":"paid","due_date":"2026-04-16T10:00:00.000Z","external_source":null,"external_type":null,"external_id":null,"hosted_invoice_url":"https://pay.revkeen.com/inv_example_0042","created_at":"2026-04-16T10:00:00.000Z","updated_at":"2026-04-16T10:02:00.000Z"},"requestId":"req_7f3a9c2e5b1d"}},"InvoicePdfResponse":{"type":"object","properties":{"data":{"type":"object","properties":{"url":{"type":"string","format":"uri","description":"Presigned URL that downloads the invoice PDF. It expires at expires_at and is a secret link: do not log it or store it. Request a new one when it expires.","example":"https://files.revkeen.com/invoices/example.pdf?X-Amz-Expires=300"},"expires_at":{"type":"string","format":"date-time","description":"When the URL stops working (ISO 8601). Five minutes after the response.","example":"2026-10-09T10:05:00.000Z"}},"required":["url","expires_at"]},"requestId":{"type":"string"}},"required":["data","requestId"],"description":"A short-lived download link for an invoice PDF.","example":{"data":{"url":"https://files.revkeen.com/invoices/example-inv-0042.pdf?X-Amz-Expires=300","expires_at":"2026-04-16T10:07:00.000Z"},"requestId":"req_7f3a9c2e5b1d"}},"NotFoundError":{"type":"object","properties":{"error":{"type":"object","properties":{"type":{"type":"string","enum":["invalid_request_error"]},"code":{"type":"string","enum":["resource_not_found"]},"message":{"type":"string"},"param":{"type":"string"},"request_id":{"type":"string"}},"required":["type","code","message"]}},"required":["error"],"description":"Returned when the requested resource does not exist or does not belong to this merchant."},"InvoicePdfNotReadyError":{"type":"object","properties":{"error":{"type":"object","properties":{"type":{"type":"string","enum":["invalid_request_error"]},"code":{"type":"string","enum":["invoice_pdf_not_ready"]},"message":{"type":"string"},"request_id":{"type":"string"}},"required":["type","code","message"]}},"required":["error"],"description":"Returned when the invoice has no rendered PDF yet, for example a draft or an open invoice. Retry later; this request never renders one."},"RateLimitError":{"type":"object","properties":{"error":{"type":"object","properties":{"type":{"type":"string","enum":["rate_limit_error"]},"code":{"type":"string","enum":["rate_limit_exceeded"]},"message":{"type":"string"},"request_id":{"type":"string"},"details":{"type":"object","properties":{"retry_after":{"type":"number"}}}},"required":["type","code","message"]}},"required":["error"],"description":"Returned when the request rate exceeds the allowed limit. Retry after the period specified in details.retry_after."},"InternalError":{"type":"object","properties":{"error":{"type":"object","properties":{"type":{"type":"string","enum":["api_error"]},"code":{"type":"string","enum":["internal_error","gateway_error"]},"message":{"type":"string"},"request_id":{"type":"string"}},"required":["type","code","message"]}},"required":["error"],"description":"Returned when an unexpected server-side error occurs. If this persists, contact support with the request_id."},"ValidationError":{"type":"object","properties":{"error":{"type":"object","properties":{"type":{"type":"string","enum":["invalid_request_error"]},"code":{"type":"string","enum":["validation_error"]},"message":{"type":"string"},"param":{"type":"string"},"details":{"type":"object","properties":{"fields":{"type":"object","additionalProperties":{"type":"array","items":{"type":"string"}},"description":"Field-specific validation errors"}}},"request_id":{"type":"string"}},"required":["type","code","message"]}},"required":["error"],"description":"Returned when request parameters or body fail schema validation. The details.fields object maps each invalid parameter to its validation errors."},"SuccessResponse":{"type":"object","properties":{"success":{"type":"boolean"}},"required":["success"],"description":"Generic success confirmation for operations that do not return a resource."},"MarkDeliveredResponse":{"type":"object","properties":{"success":{"type":"boolean"},"marked_delivered":{"type":"integer","minimum":0},"reminder_eligible_now":{"type":"integer","minimum":0,"description":"Count of invoices that would match the default overdue ladder (day 1+) at the next hourly reminder cron. With anchor = NOW() on mark, this is typically 0 — the first reminder fires tomorrow."},"reminder_eligible_within_7_days":{"type":"integer","minimum":0,"description":"Count of invoices that will receive at least one reminder within 7 days of this attestation, assuming the default ladder [1, 7, 14, 30]."},"skipped":{"type":"array","items":{"type":"object","properties":{"invoice_id":{"type":"string","format":"uuid"},"reason":{"type":"string"}},"required":["invoice_id","reason"]}},"requestId":{"type":"string"}},"required":["success","marked_delivered","reminder_eligible_now","reminder_eligible_within_7_days","skipped","requestId"]},"InvoiceCommentListDetailResponse":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/InvoiceCommentDetail"}},"requestId":{"type":"string"}},"required":["data","requestId"],"description":"A list of invoice comments with extended author details."},"InvoiceCommentDetail":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"invoiceId":{"type":"string","format":"uuid"},"userId":{"type":["string","null"]},"content":{"type":"string"},"isInternal":{"type":"boolean"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":["string","null"],"format":"date-time"}},"required":["id","invoiceId","userId","content","isInternal","createdAt","updatedAt"],"description":"An invoice comment with extended author details including name, email, and avatar."},"InvoiceCommentDetailResponse":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/InvoiceCommentDetail"},"requestId":{"type":"string"}},"required":["data","requestId"],"description":"A single invoice comment with extended author details."},"ExternalUpsertResponse":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Invoice"},"created":{"type":"boolean"},"warnings":{"type":"array","items":{"type":"string"}},"requestId":{"type":"string"}},"required":["data","created","requestId"],"description":"Response from an external system upsert, indicating whether the invoice was created or updated along with any warnings."},"StaleUpdateResponse":{"type":"object","properties":{"ignored":{"type":"boolean","enum":[true]},"reason":{"type":"string","enum":["stale_update"]},"stored_timestamp":{"type":"string","format":"date-time"},"incoming_timestamp":{"type":"string","format":"date-time"},"requestId":{"type":"string"}},"required":["ignored","reason","stored_timestamp","incoming_timestamp","requestId"],"description":"Returned when an external upsert is skipped because the incoming timestamp is older than the stored version."},"UnchangedContentResponse":{"type":"object","properties":{"ignored":{"type":"boolean","enum":[true]},"reason":{"type":"string","enum":["unchanged_content"]},"content_hash":{"type":"string"},"requestId":{"type":"string"}},"required":["ignored","reason","content_hash","requestId"],"description":"Returned when an external upsert is skipped because the substantive content hash matched the stored row — no DB write performed."},"ConflictError":{"type":"object","properties":{"error":{"type":"object","properties":{"type":{"type":"string","enum":["invalid_request_error","idempotency_error"]},"code":{"type":"string","enum":["resource_already_exists","idempotency_conflict","invoice_already_paid","invoice_already_voided","invoice_already_finalized","subscription_already_canceled","order_already_canceled"]},"message":{"type":"string"},"param":{"type":"string"},"request_id":{"type":"string"},"details":{"type":"object","additionalProperties":true,"description":"Additional conflict details"}},"required":["type","code","message"]}},"required":["error"],"description":"Returned when the operation conflicts with the current resource state, such as paying an already-paid invoice or an idempotency key collision."},"InvoiceCollectionAcceptedResponse":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Invoice"},"collection":{"$ref":"#/components/schemas/InvoiceCollection"},"requestId":{"type":"string"}},"required":["data","collection","requestId"],"description":"The invoice as it now stands (still unpaid) and the Direct Debit collection that was scheduled for it.","example":{"data":{"id":"f1a2b3c4-d5e6-4270-8a9b-0c1d2e3f4a21","customer_uuid":"c2a1e7d4-5b3f-4a8e-9d21-6f0b3e4c7a11","invoice_number":"INV-0042","total_minor":5880,"currency":"GBP","allowed_methods":["card","direct_debit"],"status":"open","due_date":"2026-04-16T10:00:00.000Z","external_source":null,"external_type":null,"external_id":null,"hosted_invoice_url":"https://pay.revkeen.com/inv_example_0042","created_at":"2026-04-16T10:00:00.000Z","updated_at":"2026-04-16T10:01:00.000Z"},"collection":{"status":"scheduled","payment_method":"direct_debit","id":"0a1b2c3d-4e5f-4a76-8b87-9c0d1e2f3a75","mandate_id":"6a7b8c9d-0e1f-4a32-8c3d-4e5f6a7b8c31","amount_minor":5880,"currency":"GBP","collection_date":"2026-05-08","notice_date":"2026-04-24","submission_date":"2026-05-05"},"requestId":"req_7f3a9c2e5b1d"}},"InvoiceCollection":{"type":"object","properties":{"status":{"type":"string","enum":["scheduled","awaiting_advance_notice"],"description":"`scheduled`: the collection is with the Direct Debit bureau for `collection_date`. `awaiting_advance_notice`: the customer's advance notice has been sent and the collection is submitted once its delivery is confirmed; the dates are not yet fixed.","example":"scheduled"},"payment_method":{"type":"string","enum":["direct_debit"]},"id":{"type":["string","null"],"format":"uuid","description":"The collection schedule id, once scheduled."},"mandate_id":{"type":["string","null"],"format":"uuid"},"amount_minor":{"type":["integer","null"],"description":"The amount being collected, in minor units, once scheduled."},"currency":{"type":["string","null"]},"collection_date":{"type":["string","null"],"description":"The date the money is collected (YYYY-MM-DD), once scheduled."},"notice_date":{"type":["string","null"]},"submission_date":{"type":["string","null"]}},"required":["status","payment_method","id","mandate_id","amount_minor","currency","collection_date","notice_date","submission_date"],"description":"A Direct Debit collection scheduled for an invoice. The invoice is not paid until the collection settles.","example":{"status":"scheduled","payment_method":"direct_debit","id":"0a1b2c3d-4e5f-4a76-8b87-9c0d1e2f3a75","mandate_id":"6a7b8c9d-0e1f-4a32-8c3d-4e5f6a7b8c31","amount_minor":5880,"currency":"GBP","collection_date":"2026-05-08","notice_date":"2026-04-24","submission_date":"2026-05-05"}},"MerchantActionForbiddenError":{"anyOf":[{"$ref":"#/components/schemas/ForbiddenError"},{"$ref":"#/components/schemas/MerchantAuthorizationDeniedError"}]},"MerchantAuthorizationDeniedError":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"error":{"type":"string"},"action":{"type":"string"},"resourceType":{"type":"string"},"reason":{"type":"string"}},"required":["success","error","action","resourceType"],"description":"Returned when the merchant authorization policy denies this action for the caller."},"InvoicePayConflictError":{"type":"object","properties":{"error":{"type":"object","properties":{"type":{"type":"string","enum":["invalid_request_error"]},"code":{"type":"string","enum":["invoice_already_paid","invoice_already_voided","invoice_not_finalized","invoice_locked","invoice_has_pending_dd_collection","invoice_not_collectable","mandate_not_usable","direct_debit_not_configured"]},"message":{"type":"string"},"param":{"type":"string"},"request_id":{"type":"string"}},"required":["type","code","message"]}},"required":["error"],"description":"Returned when the invoice cannot take this payment or collection: it is already paid, void, a draft, not recordable or collectable here, a Direct Debit collection for it is still pending, the customer has no usable Direct Debit mandate, or Direct Debit is not enabled. Nothing is changed."},"ApiError":{"type":"object","properties":{"error":{"type":"object","properties":{"type":{"type":"string","enum":["invalid_request_error","authentication_error","authorization_error","card_error","rate_limit_error","api_error","idempotency_error"],"description":"The type of error returned","example":"invalid_request_error"},"code":{"type":"string","description":"Machine-readable error code for programmatic handling","example":"resource_not_found"},"message":{"type":"string","description":"Human-readable error message","example":"No such invoice: inv_xxx"},"param":{"type":"string","description":"The parameter that caused the error","example":"customer_id"},"status_code":{"type":"integer","description":"HTTP status code","example":404},"request_id":{"type":"string","description":"Unique request identifier for debugging","example":"req_abc123xyz"},"details":{"type":"object","additionalProperties":true,"description":"Additional context about the error"},"decline_code":{"type":"string","description":"Card decline code from the payment processor","example":"insufficient_funds"},"doc_url":{"type":"string","format":"uri","description":"URL to documentation about this error","example":"https://docs.revkeen.com/errors#resource_not_found"}},"required":["type","code","message"]}},"required":["error"],"description":"The standard error response envelope. All API errors follow this structure with a machine-readable type and code for programmatic handling, and a human-readable message for debugging."},"AuthorizationAuditUnavailableError":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"error":{"type":"string"}},"required":["success","error"],"description":"Returned when a required audit row (an authorization decision, or a lifecycle row written before the effect) could not be written. The action was not performed; retry later."},"PayInvoiceRequest":{"oneOf":[{"$ref":"#/components/schemas/RecordInvoicePaymentRequest"},{"$ref":"#/components/schemas/CollectInvoicePaymentRequest"}],"discriminator":{"propertyName":"mode","mapping":{"record":"#/components/schemas/RecordInvoicePaymentRequest","collect":"#/components/schemas/CollectInvoicePaymentRequest"}},"description":"Request body for POST /v2/invoices/{id}/pay. `mode` selects the operation: `record` records a payment made outside RevKeen; `collect` schedules a Direct Debit collection."},"RecordInvoicePaymentRequest":{"type":"object","properties":{"mode":{"type":"string","enum":["record"],"description":"Record a payment the customer made outside RevKeen (bank transfer, cash, cheque). RevKeen does not collect any money in this mode.","example":"record"},"payment_method":{"type":"string","enum":["cash","check","bank_transfer","other"],"description":"How the customer paid. Required.","example":"bank_transfer"},"reference":{"type":"string","minLength":1,"maxLength":255,"description":"Your reference for the payment (cheque number, bank transfer id, receipt number). Required. It is stored on the payment record.","example":"CHK-12345"},"amount_minor":{"type":"integer","exclusiveMinimum":0,"description":"Amount received, in minor units of the invoice currency (for example pence). Required, a positive integer, never defaulted. It cannot exceed the invoice total less what is already paid. A smaller amount records a partial payment and moves the invoice to partially_paid.","example":10000},"notes":{"type":"string","maxLength":1000,"description":"Free-text note about the payment. Stored on the payment record.","example":"Received via bank wire on 15 Jan"},"paid_at":{"type":"string","format":"date-time","description":"When the money was received (ISO 8601). Defaults to now. It cannot be in the future.","example":"2026-01-15T10:30:00Z"}},"required":["mode","payment_method","reference","amount_minor"],"description":"Record a payment received outside RevKeen against an invoice."},"CollectInvoicePaymentRequest":{"type":"object","properties":{"mode":{"type":"string","enum":["collect"],"description":"Collect the invoice's outstanding amount from the customer. The collection is scheduled, not completed: the invoice stays unpaid until the money settles.","example":"collect"},"payment_method":{"type":"string","enum":["direct_debit"],"description":"How to collect. Required. Only `direct_debit` is available; card collection is not available through this operation.","example":"direct_debit"}},"required":["mode","payment_method"],"additionalProperties":false,"description":"Collect an invoice's outstanding amount by Direct Debit."},"CreditEligibilityResponse":{"type":"object","properties":{"data":{"type":"object","properties":{"can_credit":{"type":"boolean","description":"Whether a credit note can be issued for this invoice"},"max_creditable_minor":{"type":"integer","description":"Maximum amount that can be credited in minor units"},"total_credited_minor":{"type":"integer","description":"Total amount already credited against this invoice"},"invoice_total_minor":{"type":"integer","description":"Original invoice total in minor units"},"reason":{"type":"string","description":"Reason why the invoice cannot be credited, if applicable"}},"required":["can_credit","max_creditable_minor","total_credited_minor","invoice_total_minor"]},"requestId":{"type":"string"}},"required":["data","requestId"],"description":"Shows whether an invoice is eligible for a credit note and the maximum amount that can be credited."},"InvoiceCreditNoteListResponse":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/InvoiceCreditNote"}},"requestId":{"type":"string"}},"required":["data","requestId"],"description":"A list of credit notes matching the query filters."},"InvoiceCreditNote":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Unique credit note identifier"},"credit_note_number":{"type":["string","null"],"description":"Sequential credit note number for accounting reference"},"invoice_id":{"type":"string","format":"uuid","description":"ID of the invoice this credit note applies to"},"customer_id":{"type":["string","null"],"format":"uuid","description":"ID of the customer who received the credit"},"amount_minor":{"type":"integer","description":"Credit amount in minor units (cents)"},"tax_amount_minor":{"type":["integer","null"],"description":"Tax portion of the credit in minor units"},"currency":{"type":"string","description":"Three-letter ISO 4217 currency code"},"status":{"type":"string","description":"Credit note status: draft, issued, or void"},"reason":{"type":["string","null"],"description":"Human-readable reason for the credit"},"reason_code":{"type":["string","null"],"description":"Machine-readable reason code (e.g., billing_error, customer_request)"},"credit_method":{"type":["string","null"],"description":"How the credit is applied: refund_to_payment_method, customer_balance, or external"},"pdf_url":{"type":["string","null"],"description":"URL to the credit note PDF document"},"issued_at":{"type":["string","null"],"format":"date-time","description":"When the credit note was issued (ISO 8601)"},"created_at":{"type":"string","format":"date-time","description":"When the credit note was created (ISO 8601)"},"updated_at":{"type":"string","format":"date-time","description":"When the credit note was last updated (ISO 8601)"}},"required":["id","credit_note_number","invoice_id","customer_id","amount_minor","tax_amount_minor","currency","status","reason","reason_code","credit_method","pdf_url","issued_at","created_at","updated_at"],"description":"A formal accounting document that reduces the amount owed on a paid or partially paid invoice, used for refunds, billing corrections, and prorated cancellations."},"CreditNoteResponse":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/CreditNote"},"requestId":{"type":"string"}},"required":["data","requestId"],"description":"A single credit note with request tracking metadata."},"CreditNote":{"type":"object","properties":{"id":{"type":"string","format":"uuid","example":"550e8400-e29b-41d4-a716-446655440000"},"credit_note_number":{"type":["string","null"],"example":"CN-00042"},"invoice_id":{"type":"string","format":"uuid","example":"660e8400-e29b-41d4-a716-446655440000","description":"The invoice this credit note is for"},"customer_id":{"type":["string","null"],"format":"uuid","example":"770e8400-e29b-41d4-a716-446655440000"},"amount_minor":{"type":"integer","example":5000,"description":"Credit amount in cents"},"tax_amount_minor":{"type":["integer","null"],"example":500,"description":"Tax portion of the credit in cents"},"currency":{"type":"string","example":"USD"},"credit_method":{"type":["string","null"],"enum":["refund_to_payment_method","customer_balance","external",null],"example":"refund_to_payment_method"},"reason":{"type":["string","null"],"example":"Service not provided as agreed"},"reason_code":{"type":["string","null"],"enum":["customer_request","duplicate_charge","service_issue","billing_error","subscription_cancellation","proration","other",null],"example":"customer_request"},"status":{"type":"string","enum":["draft","pending","issued","applied","voided"],"example":"issued"},"refund_id":{"type":["string","null"],"format":"uuid","example":null,"description":"Associated refund ID if credit method is refund_to_payment_method"},"subscription_id":{"type":["string","null"],"format":"uuid","example":null},"subscription_canceled":{"type":"boolean","example":false},"is_prorated":{"type":"boolean","example":false},"pdf_url":{"type":["string","null"],"example":null},"issued_at":{"type":["string","null"],"format":"date-time","example":"2024-01-15T10:30:00Z"},"created_at":{"type":"string","format":"date-time","example":"2024-01-14T08:00:00Z"},"updated_at":{"type":"string","format":"date-time","example":"2024-01-15T10:30:00Z"}},"required":["id","credit_note_number","invoice_id","customer_id","amount_minor","tax_amount_minor","currency","credit_method","reason","reason_code","status","pdf_url","issued_at","created_at","updated_at"],"description":"A credit note formally reduces the amount owed on an invoice. It is the accounting counterpart of a refund — the credit note is the document, the refund is the money movement."},"CreditNoteLineAllocationInputV1":{"type":"object","properties":{"invoice_line_item_id":{"type":"string","format":"uuid","description":"Invoice line item receiving the credit"},"quantity":{"type":"integer","exclusiveMinimum":0,"description":"Credited quantity from the source invoice line","example":1},"unit_amount_minor":{"type":"integer","minimum":0,"maximum":2000000000,"description":"Credited unit amount in minor units","example":5000},"total_amount_minor":{"type":"integer","minimum":0,"maximum":2000000000,"description":"Pre-tax credited subtotal in minor units","example":5000},"tax_amount_minor":{"type":"integer","minimum":0,"maximum":2000000000,"description":"Credited tax amount in minor units","example":1000}},"required":["invoice_line_item_id","quantity","unit_amount_minor","total_amount_minor","tax_amount_minor"]},"ProductListResponse":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Product"}},"pagination":{"$ref":"#/components/schemas/Pagination"}},"required":["data","pagination"],"description":"A paginated list of products matching the query filters."},"Product":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Unique identifier (UUID)","example":"a7b3c9d2-1e4f-4b6a-8c5d-2f9e0a1b3c42"},"object":{"type":"string","enum":["product"],"description":"Object type"},"product_id":{"type":"string","description":"User-facing product identifier","example":"PRO-MONTHLY"},"name":{"type":"string","description":"Product display name"},"description":{"type":["string","null"],"description":"Product description"},"kind":{"type":"string","enum":["subscription","one_time","usage"],"description":"Product kind","example":"subscription"},"pricing_model":{"type":"string","enum":["one_time","recurring","usage"],"description":"How the product is priced","example":"recurring"},"amount_minor":{"type":["integer","null"],"description":"Price in minor units (cents/pence)","example":4900},"currency":{"type":"string","description":"Three-letter ISO currency code","example":"GBP"},"interval":{"type":["string","null"],"description":"Billing interval (day, week, month, year)"},"interval_count":{"type":["integer","null"],"description":"Number of intervals between billings"},"trial_days":{"type":"integer","description":"Free trial period in days"},"fulfillment_type":{"type":"string","enum":["none","digital","physical"],"description":"Fulfillment type"},"track_inventory":{"type":"boolean","description":"Whether stock is tracked for this product (read from its availability record)"},"stock_quantity":{"type":["integer","null"],"deprecated":true,"description":"Deprecated, read-only view: units available to sell, computed from the product's availability record (null when not tracked)."},"tags":{"type":"array","items":{"type":"string"},"description":"Tags for Storefront filters and search"},"billing_anchor_rule":{"type":["string","null"],"enum":["same_day","day_of_month","last_day",null],"description":"Billing date calculation rule"},"billing_anchor_day":{"type":["integer","null"],"description":"Day of month for billing (1-31)"},"first_charge_behavior":{"type":["string","null"],"enum":["immediate","next_anchor","prorate",null],"description":"First payment timing"},"end_behavior":{"type":["string","null"],"enum":["until_canceled","fixed_payments",null],"description":"Subscription end behavior"},"max_payments":{"type":["integer","null"],"description":"Max billing cycles for fixed-payment subscriptions"},"usage_meter_id":{"type":["string","null"],"description":"Associated usage meter ID"},"slug":{"type":["string","null"],"description":"URL handle, unique among this merchant's products"},"is_active":{"type":"boolean","description":"Whether the product is active"},"is_archived":{"type":"boolean","description":"Whether the product is archived"},"image_url":{"type":["string","null"],"description":"Main product image (storage key or URL)"},"images":{"type":"array","items":{"$ref":"#/components/schemas/ProductImageResponse"},"description":"Extra product images, in display order"},"seo_title":{"type":["string","null"],"description":"Search and social title"},"seo_description":{"type":["string","null"],"description":"Meta description"},"store_visibility":{"type":"string","enum":["visible","hidden","scheduled"],"description":"Whether the product is listed on the Storefront. hidden: only payment links and invoices. scheduled: listed from store_visible_from."},"store_visible_from":{"type":["string","null"],"format":"date-time","description":"When a scheduled product appears on the Storefront (ISO 8601)"},"tax_behavior":{"type":["string","null"],"description":"Tax behavior (exclusive, inclusive, location)"},"tax_code":{"type":["string","null"],"description":"Tax code for tax calculation"},"metadata":{"type":"object","additionalProperties":true,"description":"Custom key-value metadata. Omitted when the product has none."},"created_at":{"type":"string","format":"date-time","description":"Creation timestamp (ISO 8601)"},"updated_at":{"type":"string","format":"date-time","description":"Last update timestamp (ISO 8601)"}},"required":["id","object","product_id","name","kind","pricing_model","amount_minor","currency","trial_days","fulfillment_type","is_active","is_archived","created_at","updated_at"],"description":"A product defines what you sell — the name, billing type, price, and fulfillment method. Products can be one-time, recurring (subscription), or usage-based.","example":{"id":"a7b3c9d2-1e4f-4b6a-8c5d-2f9e0a1b3c42","object":"product","product_id":"PRO-MONTHLY","name":"Pro plan","description":"Unlimited invoices, automated reminders and priority support.","kind":"subscription","pricing_model":"recurring","amount_minor":4900,"currency":"GBP","interval":"month","interval_count":1,"trial_days":14,"fulfillment_type":"digital","track_inventory":false,"stock_quantity":null,"tags":["saas","monthly"],"billing_anchor_rule":"same_day","billing_anchor_day":null,"first_charge_behavior":"immediate","end_behavior":"until_canceled","max_payments":null,"usage_meter_id":null,"slug":"pro-plan","is_active":true,"is_archived":false,"image_url":null,"images":[],"seo_title":null,"seo_description":null,"store_visibility":"visible","store_visible_from":null,"tax_behavior":"exclusive","tax_code":null,"metadata":{},"created_at":"2026-04-02T10:00:00.000Z","updated_at":"2026-04-02T10:05:00.000Z"}},"ProductImageResponse":{"type":"object","properties":{"key":{"type":"string","description":"Storage key or https URL"},"asset_id":{"type":["string","null"],"description":"Media library asset ID"},"alt":{"type":["string","null"],"description":"Alt text"},"source":{"type":"string","enum":["merchant","pexels","canva","gemini"],"description":"Where the image came from"},"photographer":{"type":["string","null"],"description":"Photographer credit"},"attribution_url":{"type":["string","null"],"description":"Link for the credit"}},"required":["key","asset_id","alt","source","photographer","attribution_url"],"description":"One extra product image, in display order."},"Pagination":{"type":"object","properties":{"kind":{"type":"string","description":"Pagination type","example":"offset"},"page":{"type":"integer","description":"Current page number","example":1},"limit":{"type":"integer","description":"Results per page","example":20},"total":{"type":"integer","description":"Total matching results","example":42},"totalPages":{"type":"integer","description":"Total pages available","example":3}},"required":["kind","page","limit","total","totalPages"]},"ProductErrorResponse":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"],"description":"Error response returned when a product operation fails. `error` is a string here, not the `{ type, code, message }` object most endpoints return. It holds the message; there is no `code`."},"ProductRetrieveResponse":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Product"}},"required":["data"],"description":"A single product object with full details and associated prices."},"ProductCreateResponse":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Product"}},"required":["data"],"description":"The newly created product object."},"CreateProductRequest":{"type":"object","properties":{"product_id":{"type":"string","minLength":1},"name":{"type":"string","minLength":1},"description":{"type":["string","null"]},"kind":{"type":"string","enum":["subscription","one_time","usage"]},"pricing_model":{"type":"string","enum":["one_time","recurring","usage"]},"amount_minor":{"type":"integer","minimum":0},"currency":{"type":"string","description":"Three-letter ISO currency code. Defaults to the merchant's default currency."},"interval":{"type":["string","null"]},"interval_count":{"type":["integer","null"]},"trial_days":{"type":"integer","minimum":0},"usage_meter_id":{"type":["string","null"]},"slug":{"type":"string","maxLength":80,"description":"URL handle, unique among this merchant's products. Normalised to lower-case letters, digits and hyphens. Derived from product_id when omitted."},"images":{"type":"array","items":{"$ref":"#/components/schemas/ProductImage"},"maxItems":20,"description":"Ordered extra images (at most 20). Replaces the list. The main image stays in image_url."},"seo_title":{"type":["string","null"],"maxLength":70,"description":"Search and social title (at most 70 characters). Defaults to the name."},"seo_description":{"type":["string","null"],"maxLength":320,"description":"Meta description (at most 320 characters)."},"store_visibility":{"type":"string","enum":["visible","hidden","scheduled"],"description":"Whether the product is listed on the Storefront. hidden: only payment links and invoices. scheduled: listed from store_visible_from."},"store_visible_from":{"type":["string","null"],"format":"date-time","description":"When a scheduled product appears on the Storefront (ISO 8601). Required when store_visibility is scheduled."},"fulfillment_type":{"type":"string","enum":["none","digital","physical"],"default":"none","description":"Fulfillment type for the product. Defaults to 'none' (service)."},"track_inventory":{"type":"boolean","default":false,"description":"Track stock for this product (one-time products only; requires stock_quantity). Stock is stored in the product's availability record, not on the product."},"stock_quantity":{"type":["integer","null"],"minimum":0,"deprecated":true,"description":"Deprecated. Sets the units available to sell in the product's availability record. Required (non-negative) when track_inventory is true."},"tags":{"type":"array","items":{"type":"string","minLength":1,"maxLength":50},"maxItems":50,"description":"Tags for Storefront filters and search. Replaces the product's tags. At most 50, each up to 50 characters."},"billing_anchor_rule":{"type":"string","enum":["same_day","day_of_month","last_day"],"description":"How billing dates are calculated. same_day: Bill on same day as start. day_of_month: Bill on specific day (1-31). last_day: Bill on last day of month."},"billing_anchor_day":{"type":["integer","null"],"minimum":1,"maximum":31,"description":"Day of month (1-31) when billing_anchor_rule is 'day_of_month'"},"first_charge_behavior":{"type":"string","enum":["immediate","next_anchor","prorate"],"description":"When first payment is collected. immediate: Charge on start. next_anchor: Charge on first scheduled date. prorate: Prorate until first date."},"end_behavior":{"type":"string","enum":["until_canceled","fixed_payments"],"description":"How subscription ends. until_canceled: Runs forever. fixed_payments: Ends after N billing cycles."},"max_payments":{"type":["integer","null"],"minimum":1,"maximum":999,"description":"Max billing cycles when end_behavior is 'fixed_payments'"},"metadata":{"type":"object","additionalProperties":{},"description":"Arbitrary key-value metadata for the product"},"image_url":{"type":["string","null"],"description":"Product image S3 key or public URL (upload via /internal/storage first)"}},"required":["product_id","name","kind","pricing_model","amount_minor"],"description":"Parameters for creating a new product, including name, billing type, and pricing."},"ProductImage":{"type":"object","properties":{"key":{"type":"string","minLength":1,"maxLength":2048,"description":"Storage key returned by the upload (product-images/<merchant>/...) or an https URL."},"asset_id":{"type":["string","null"],"format":"uuid","description":"Media library asset ID, when the image came from an upload."},"alt":{"type":["string","null"],"maxLength":300,"description":"Alt text for shoppers using screen readers."},"source":{"type":"string","enum":["merchant","pexels","canva","gemini"],"description":"Where the image came from. Defaults to merchant. Pexels images must keep photographer and attribution_url."},"photographer":{"type":["string","null"],"maxLength":160,"description":"Photographer credit (required for Pexels)."},"attribution_url":{"type":["string","null"],"maxLength":2000,"description":"https link for the credit (required for Pexels)."}},"required":["key"],"description":"One extra product image. The main image is image_url and is never repeated in images."},"ProductUpdateResponse":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Product"}},"required":["data"],"description":"The updated product object reflecting the changes."},"UpdateProductRequest":{"type":"object","properties":{"name":{"type":"string","minLength":1},"description":{"type":["string","null"]},"amount_minor":{"type":"integer","minimum":0},"currency":{"type":"string"},"interval":{"type":["string","null"]},"interval_count":{"type":["integer","null"]},"trial_days":{"type":"integer","minimum":0},"is_active":{"type":"boolean"},"is_archived":{"type":"boolean"},"slug":{"type":["string","null"],"maxLength":80,"description":"URL handle, unique among this merchant's products. Pass null to clear it."},"images":{"type":"array","items":{"$ref":"#/components/schemas/ProductImage"},"maxItems":20,"description":"Ordered extra images (at most 20). Replaces the list. The main image stays in image_url."},"seo_title":{"type":["string","null"],"maxLength":70,"description":"Search and social title (at most 70 characters). Defaults to the name."},"seo_description":{"type":["string","null"],"maxLength":320,"description":"Meta description (at most 320 characters)."},"store_visibility":{"type":"string","enum":["visible","hidden","scheduled"],"description":"Whether the product is listed on the Storefront. hidden: only payment links and invoices. scheduled: listed from store_visible_from."},"store_visible_from":{"type":["string","null"],"format":"date-time","description":"When a scheduled product appears on the Storefront (ISO 8601). Required when store_visibility is scheduled."},"fulfillment_type":{"type":"string","enum":["none","digital","physical"],"description":"Fulfillment type for the product."},"track_inventory":{"type":"boolean","description":"Turn stock tracking on (requires stock_quantity) or off. One-time products only. Stock is stored in the product's availability record."},"stock_quantity":{"type":["integer","null"],"minimum":0,"deprecated":true,"description":"Deprecated. Sets the units available to sell in the product's availability record. Required (non-negative) when track_inventory is true."},"tags":{"type":"array","items":{"type":"string","minLength":1,"maxLength":50},"maxItems":50,"description":"Tags for Storefront filters and search. Replaces the product's tags. At most 50, each up to 50 characters."},"billing_anchor_rule":{"type":"string","enum":["same_day","day_of_month","last_day"],"description":"How billing dates are calculated. same_day: Bill on same day as start. day_of_month: Bill on specific day (1-31). last_day: Bill on last day of month."},"billing_anchor_day":{"type":["integer","null"],"minimum":1,"maximum":31},"first_charge_behavior":{"type":"string","enum":["immediate","next_anchor","prorate"],"description":"When first payment is collected. immediate: Charge on start. next_anchor: Charge on first scheduled date. prorate: Prorate until first date."},"end_behavior":{"type":"string","enum":["until_canceled","fixed_payments"],"description":"How subscription ends. until_canceled: Runs forever. fixed_payments: Ends after N billing cycles."},"max_payments":{"type":["integer","null"],"minimum":1,"maximum":999},"metadata":{"type":"object","additionalProperties":{},"description":"Arbitrary key-value metadata for the product"},"image_url":{"type":["string","null"],"description":"Product image S3 key or public URL. Pass null to remove."}},"description":"Parameters for updating an existing product. Only the provided fields are changed."},"ProductCollectionListResponse":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/ProductCollection"}},"has_more":{"type":"boolean"},"limit":{"type":"integer"},"offset":{"type":"integer"}},"required":["data","has_more","limit","offset"]},"ProductCollection":{"type":"object","properties":{"id":{"type":"string"},"object":{"type":"string","enum":["product_collection"]},"name":{"type":"string"},"slug":{"type":"string","description":"URL handle, unique per merchant."},"description":{"type":["string","null"]},"image_asset_id":{"type":["string","null"]},"position":{"type":"integer"},"version":{"type":"integer","description":"Increments on every change. Send it back as expected_version to avoid overwriting someone else's edit."},"product_ids":{"type":"array","items":{"type":"string"},"description":"Member product ids, in display order."},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"}},"required":["id","object","name","slug","description","image_asset_id","position","version","product_ids","created_at","updated_at"],"description":"A merchant-defined group of products, shown on the Storefront."},"ProductCollectionError":{"type":"object","properties":{"error":{"type":"string"},"code":{"type":"string"},"product_ids":{"type":"array","items":{"type":"string"}}},"required":["error"],"description":"`error` is a string here, not the `{ type, code, message }` object most endpoints return. It holds the message; `code` is the machine-readable code to branch on, and a 422 for unknown products also lists `product_ids`."},"ProductCollectionResponse":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/ProductCollection"}},"required":["data"]},"ProductCollectionCreateResponse":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/ProductCollection"}},"required":["data"]},"ProductCollectionCreateRequest":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":120},"slug":{"type":"string","minLength":1,"maxLength":80,"pattern":"^[a-z0-9]+(-[a-z0-9]+)*$"},"description":{"type":["string","null"],"maxLength":2000},"image_asset_id":{"type":["string","null"],"format":"uuid"},"position":{"type":"integer","minimum":0,"maximum":100000},"product_ids":{"type":"array","items":{"type":"string","format":"uuid"},"maxItems":500,"description":"Member product ids, in display order. Replaces the membership when present."}},"required":["name","slug"],"additionalProperties":false},"ProductCollectionUpdateResponse":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/ProductCollection"}},"required":["data"]},"ProductCollectionUpdateRequest":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":120},"slug":{"type":"string","minLength":1,"maxLength":80,"pattern":"^[a-z0-9]+(-[a-z0-9]+)*$"},"description":{"type":["string","null"],"maxLength":2000},"image_asset_id":{"type":["string","null"],"format":"uuid"},"position":{"type":"integer","minimum":0,"maximum":100000},"product_ids":{"type":"array","items":{"type":"string","format":"uuid"},"maxItems":500,"description":"Member product ids, in display order. Replaces the membership when present."},"expected_version":{"type":"integer","exclusiveMinimum":0}},"additionalProperties":false},"ProductCollectionDeletedResponse":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/ProductCollectionDeleted"}},"required":["data"]},"ProductCollectionDeleted":{"type":"object","properties":{"id":{"type":"string"},"object":{"type":"string","enum":["product_collection"]},"deleted":{"type":"boolean","enum":[true]}},"required":["id","object","deleted"]},"CheckoutSessionCreateResponse":{"type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string"},"url":{"type":"string"},"public_token":{"type":"string"},"amount_minor":{"type":"integer"},"currency":{"type":"string"},"customer_id":{"type":"string"},"expires_at":{"type":"string","format":"date-time"},"allowed_methods":{"type":"array","items":{"type":"string"},"example":["card","direct_debit"]},"selected_method":{"type":["string","null"],"example":null}},"required":["id","url","public_token","currency","expires_at"]},"requestId":{"type":"string"}},"required":["data","requestId"],"description":"The created checkout session with the URL to redirect the customer to."},"CheckoutSessionErrorResponse":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"],"description":"Error response returned when a checkout session cannot be created."},"NoPaymentMethodsAvailableError":{"type":"object","properties":{"error":{"type":"string","example":"No payment rails available for this merchant. Configure at least one payment method."},"code":{"type":"string","enum":["NO_PAYMENT_METHODS_AVAILABLE"]}},"required":["error","code"],"description":"Returned when the merchant has no online payment method available for this checkout (none enabled and live, or none valid in the requested currency). Configure a payment method, or use a currency an enabled method supports, then retry."},"CreateCheckoutSessionInput":{"type":"object","properties":{"invoice_id":{"type":"string","format":"uuid","example":"11111111-1111-4111-8111-111111111111"},"currency":{"type":"string","example":"USD","description":"Three-letter ISO currency code. Defaults to the merchant's default currency."},"success_url":{"type":"string","format":"uri","example":"https://example.com/success"},"cancel_url":{"type":"string","format":"uri","example":"https://example.com/cancel"},"allowed_methods":{"type":"array","items":{"type":"string","enum":["card","direct_debit","in_store"]},"example":["card","direct_debit"],"description":"Payment methods to offer. Intersected with merchant capabilities. Defaults to merchant config."},"companion_device_id":{"type":"string","format":"uuid","description":"Target a registered companion device. Session is pushed via SSE to the device."}},"required":["invoice_id"],"additionalProperties":false,"description":"Parameters for creating a hosted checkout session. Customers are redirected to complete payment on a RevKeen-hosted page."},"CheckoutSessionRetrieveResponse":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/CheckoutSession"},"requestId":{"type":"string"}},"required":["data","requestId"]},"CheckoutSession":{"type":"object","properties":{"id":{"type":"string","format":"uuid","example":"e1f2a3b4-c5d6-4e7f-8a9b-0c1d2e3f4a55"},"object":{"type":"string","enum":["checkout_session"]},"status":{"type":"string","example":"pending"},"mode":{"type":["string","null"],"example":"subscription"},"amount_total":{"type":["integer","null"],"example":4900},"currency":{"type":["string","null"],"example":"GBP"},"customer_id":{"type":["string","null"],"format":"uuid","example":"c2a1e7d4-5b3f-4a8e-9d21-6f0b3e4c7a11"},"payment_intent_id":{"type":["string","null"],"format":"uuid"},"invoice_id":{"type":["string","null"],"format":"uuid"},"subscription_id":{"type":["string","null"],"format":"uuid"},"url":{"type":["string","null"],"description":"The hosted checkout URL to redirect the customer to. Present only while the session is `pending` or `open` and has not expired, and only when the request is made with a merchant API key; otherwise `null`."},"success_url":{"type":["string","null"]},"cancel_url":{"type":["string","null"]},"expires_at":{"type":["string","null"],"format":"date-time"},"created_at":{"type":["string","null"],"format":"date-time"},"allowed_methods":{"type":"array","items":{"type":"string"},"example":["card","in_store"]},"selected_method":{"type":["string","null"],"example":null}},"required":["id","object","status","mode","amount_total","currency","customer_id","payment_intent_id","invoice_id","subscription_id","url","success_url","cancel_url","expires_at","created_at","allowed_methods","selected_method"],"description":"A checkout session represents a customer's intent to pay.","example":{"id":"e1f2a3b4-c5d6-4e7f-8a9b-0c1d2e3f4a55","object":"checkout_session","status":"pending","mode":"subscription","amount_total":4900,"currency":"GBP","customer_id":"c2a1e7d4-5b3f-4a8e-9d21-6f0b3e4c7a11","payment_intent_id":null,"invoice_id":null,"subscription_id":null,"url":"https://checkout.revkeen.com/p/rvk_cs_2xK9mQ4vLp8RwT3nYb6Hc","success_url":"https://example.com/checkout/success","cancel_url":"https://example.com/checkout/cancel","expires_at":"2026-04-03T10:00:00.000Z","created_at":"2026-04-02T10:00:00.000Z","allowed_methods":["card"],"selected_method":null}},"CheckoutSessionExpireResponse":{"type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"status":{"type":"string","enum":["expired"]},"expired_at":{"type":"string","format":"date-time"}},"required":["id","status","expired_at"]},"requestId":{"type":"string"}},"required":["data","requestId"]},"CartSessionResponse":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/CartSession"}},"required":["data"]},"CartSession":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"object":{"type":"string","enum":["cart_session"]},"merchant_id":{"type":"string","format":"uuid"},"customer_id":{"type":["string","null"],"format":"uuid"},"currency":{"type":"string","minLength":3,"maxLength":3},"mode":{"type":"string","example":"payment"},"status":{"$ref":"#/components/schemas/CartSessionStatus"},"line_items":{"type":"array","items":{"$ref":"#/components/schemas/CartLineItem"}},"add_ons_offered":{"type":"array","items":{"type":"string","format":"uuid"}},"add_ons_selected":{"type":"array","items":{"type":"string","format":"uuid"}},"discount_code":{"type":["string","null"]},"email":{"type":["string","null"],"format":"email","description":"Captured customer email for recovery, if provided."},"promotional_consent":{"type":["boolean","null"]},"sms_consent":{"type":["boolean","null"]},"subtotal_minor":{"type":"integer","minimum":0},"total_minor":{"type":"integer","minimum":0},"metadata":{"type":["object","null"],"additionalProperties":{}},"converted_to_checkout_session_id":{"type":["string","null"],"format":"uuid"},"public_token":{"type":"string","description":"Anon-access token. Use to build /c/[cart_session_id]?token=... URLs for customer-facing cart checkout. Open carts can be reviewed and edited; unexpired abandoned carts can be reviewed and converted as-is. Expired and converted carts are inaccessible."},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"},"expires_at":{"type":"string","format":"date-time"}},"required":["id","object","merchant_id","customer_id","currency","mode","status","line_items","add_ons_offered","add_ons_selected","discount_code","email","promotional_consent","sms_consent","subtotal_minor","total_minor","metadata","converted_to_checkout_session_id","public_token","created_at","updated_at","expires_at"],"example":{"id":"8c9d0e1f-2a3b-4c54-8e5f-6a7b8c9d0e53","object":"cart_session","merchant_id":"8f14e45f-ceea-4e7a-9b4b-7c3f1a2d5e60","customer_id":"c2a1e7d4-5b3f-4a8e-9d21-6f0b3e4c7a11","currency":"GBP","mode":"subscription","status":"open","line_items":[{"id":"cli_3f6c2a1e-8b4d-4e7f-9a2c-5d1e0f3b6a74","product_id":"a7b3c9d2-1e4f-4b6a-8c5d-2f9e0a1b3c42","name":"Pro plan","quantity":1,"unit_price_minor":4900,"currency":"GBP","recurring":{"interval":"month","interval_count":1},"billing_max_cycles":null,"trial_period_days":14,"start_rule":"immediate","billing_anchor_rule":"same_day","billing_anchor_day":null,"due_today_minor":0,"first_charge_minor":4900,"first_renewal_at":"2026-05-02T09:00:00.000Z","effective_start_rule":"immediate","metadata":null}],"add_ons_offered":[],"add_ons_selected":[],"discount_code":null,"email":"ada.lovelace@example.com","promotional_consent":false,"sms_consent":false,"subtotal_minor":4900,"total_minor":4900,"metadata":null,"converted_to_checkout_session_id":null,"public_token":"cart_tok_example_7q2k9m","created_at":"2026-04-18T09:00:00.000Z","updated_at":"2026-04-18T09:00:00.000Z","expires_at":"2026-04-19T09:00:00.000Z"}},"CartSessionStatus":{"type":"string","enum":["open","converted","abandoned","expired"]},"CartLineItem":{"type":"object","properties":{"id":{"type":"string","example":"cli_550e8400-e29b-41d4-a716-446655440000"},"product_id":{"type":"string","format":"uuid"},"name":{"type":"string"},"quantity":{"type":"integer","exclusiveMinimum":0},"unit_price_minor":{"type":"integer","minimum":0},"currency":{"type":"string","minLength":3,"maxLength":3},"recurring":{"$ref":"#/components/schemas/CartLineItemRecurring"},"billing_max_cycles":{"type":["integer","null"],"exclusiveMinimum":0},"trial_period_days":{"type":["integer","null"],"minimum":0},"start_rule":{"$ref":"#/components/schemas/CartStartRule"},"billing_anchor_rule":{"$ref":"#/components/schemas/CartBillingAnchorRule"},"billing_anchor_day":{"type":["integer","null"],"minimum":1,"maximum":31},"due_today_minor":{"type":["integer","null"],"minimum":0},"first_charge_minor":{"type":["integer","null"],"minimum":0},"first_renewal_at":{"type":["string","null"],"format":"date-time"},"effective_start_rule":{"$ref":"#/components/schemas/CartStartRule"},"metadata":{"type":["object","null"],"additionalProperties":{}}},"required":["id","product_id","name","quantity","unit_price_minor","currency","recurring"],"example":{"id":"cli_3f6c2a1e-8b4d-4e7f-9a2c-5d1e0f3b6a74","product_id":"a7b3c9d2-1e4f-4b6a-8c5d-2f9e0a1b3c42","name":"Pro plan","quantity":1,"unit_price_minor":4900,"currency":"GBP","recurring":{"interval":"month","interval_count":1},"billing_max_cycles":null,"trial_period_days":14,"start_rule":"immediate","billing_anchor_rule":"same_day","billing_anchor_day":null,"due_today_minor":0,"first_charge_minor":4900,"first_renewal_at":"2026-05-02T09:00:00.000Z","effective_start_rule":"immediate","metadata":null}},"CartLineItemRecurring":{"type":["object","null"],"properties":{"interval":{"type":"string","enum":["day","week","month","year"],"example":"month"},"interval_count":{"type":"integer","exclusiveMinimum":0,"example":1}},"required":["interval","interval_count"]},"CartStartRule":{"type":["string","null"],"enum":["immediate","next_anchor","prorate",null]},"CartBillingAnchorRule":{"type":["string","null"],"enum":["same_day","day_of_month","last_day",null]},"CartSessionErrorResponse":{"type":"object","properties":{"error":{"type":"string","example":"Cart session not found"},"code":{"type":"string","enum":["CART_SESSION_NOT_FOUND","CART_LINE_ITEM_NOT_FOUND","CART_SESSION_CLOSED","CART_SESSION_EMPTY"]}},"required":["error"],"description":"`error` is a string here, not the `{ type, code, message }` object most endpoints return. It holds the message; `code`, when present, is a machine-readable code. The listed values are the common ones: other codes can be returned, so treat `code` as open."},"CreateCartSessionInput":{"type":"object","properties":{"currency":{"type":"string","minLength":3,"maxLength":3,"example":"GBP"},"mode":{"type":"string","enum":["payment","subscription","mixed"],"example":"payment"},"customer_id":{"type":"string","format":"uuid"},"line_items":{"type":"array","items":{"$ref":"#/components/schemas/CartLineItemInput"}},"add_ons_offered":{"type":"array","items":{"type":"string","format":"uuid"}},"metadata":{"type":"object","additionalProperties":{}},"payment_link_id":{"type":"string","format":"uuid","description":"Seed this cart from a cart-enabled payment link."}},"required":["currency"],"additionalProperties":false},"CartLineItemInput":{"type":"object","properties":{"product_id":{"type":"string","format":"uuid","example":"11111111-1111-4111-8111-111111111111"},"name":{"type":"string","minLength":1,"maxLength":500,"example":"Annual plan"},"quantity":{"type":"integer","exclusiveMinimum":0,"example":1},"unit_price_minor":{"type":"integer","minimum":0,"example":9900,"description":"Unit price in minor units (pence, cents)."},"currency":{"type":"string","minLength":3,"maxLength":3,"example":"GBP"},"recurring":{"$ref":"#/components/schemas/CartLineItemRecurring"},"metadata":{"type":"object","additionalProperties":{}}},"required":["product_id","name","quantity","unit_price_minor","currency"]},"UpdateCartLineItemInput":{"type":"object","properties":{"quantity":{"type":"integer","exclusiveMinimum":0,"example":2}},"required":["quantity"],"additionalProperties":false},"ToggleCartAddOnInput":{"type":"object","properties":{"product_id":{"type":"string","format":"uuid","example":"11111111-1111-4111-8111-111111111111"},"selected":{"type":"boolean","example":true}},"required":["product_id","selected"],"additionalProperties":false},"ApplyCartDiscountCodeInput":{"type":"object","properties":{"code":{"type":["string","null"],"minLength":1,"maxLength":64,"example":"SAVE10","description":"Discount code to apply. Pass `null` to clear. Valid discounts are priced into cart totals before checkout handoff."}},"required":["code"],"additionalProperties":false},"SetCartContactInput":{"type":"object","properties":{"email":{"type":["string","null"],"maxLength":320,"format":"email","example":"buyer@example.com","description":"Customer email for abandoned-cart recovery. Pass `null` to clear."},"promotional_consent":{"type":["boolean","null"],"description":"Whether the customer consented to marketing/recovery email. Records the consent timestamp when set."},"sms_consent":{"type":["boolean","null"],"description":"Whether the customer consented to marketing/recovery SMS. Records the consent timestamp when set."}},"additionalProperties":false},"CartConversionResponse":{"type":"object","properties":{"data":{"type":"object","properties":{"cart_session":{"$ref":"#/components/schemas/CartSession"},"checkout_session":{"$ref":"#/components/schemas/CartCheckoutSession"}},"required":["cart_session","checkout_session"]}},"required":["data"],"example":{"data":{"cart_session":{"id":"8c9d0e1f-2a3b-4c54-8e5f-6a7b8c9d0e53","object":"cart_session","merchant_id":"8f14e45f-ceea-4e7a-9b4b-7c3f1a2d5e60","customer_id":"c2a1e7d4-5b3f-4a8e-9d21-6f0b3e4c7a11","currency":"GBP","mode":"subscription","status":"converted","line_items":[{"id":"cli_3f6c2a1e-8b4d-4e7f-9a2c-5d1e0f3b6a74","product_id":"a7b3c9d2-1e4f-4b6a-8c5d-2f9e0a1b3c42","name":"Pro plan","quantity":1,"unit_price_minor":4900,"currency":"GBP","recurring":{"interval":"month","interval_count":1},"billing_max_cycles":null,"trial_period_days":14,"start_rule":"immediate","billing_anchor_rule":"same_day","billing_anchor_day":null,"due_today_minor":0,"first_charge_minor":4900,"first_renewal_at":"2026-05-02T09:00:00.000Z","effective_start_rule":"immediate","metadata":null}],"add_ons_offered":[],"add_ons_selected":[],"discount_code":null,"email":"ada.lovelace@example.com","promotional_consent":false,"sms_consent":false,"subtotal_minor":4900,"total_minor":4900,"metadata":null,"converted_to_checkout_session_id":"9d0e1f2a-3b4c-4d65-9f6a-7b8c9d0e1f64","public_token":"cart_tok_example_7q2k9m","created_at":"2026-04-18T09:00:00.000Z","updated_at":"2026-04-18T09:05:00.000Z","expires_at":"2026-04-19T09:00:00.000Z"},"checkout_session":{"id":"9d0e1f2a-3b4c-4d65-9f6a-7b8c9d0e1f64","object":"checkout_session","merchant_id":"8f14e45f-ceea-4e7a-9b4b-7c3f1a2d5e60","customer_id":"c2a1e7d4-5b3f-4a8e-9d21-6f0b3e4c7a11","session_token":"rvk_cs_example_8m3x","checkout_url":"https://pay.revkeen.com/p/rvk_cs_example_8m3x","status":"pending","mode":"subscription","amount_minor":4900,"currency":"GBP","line_items":[{"id":"cli_3f6c2a1e-8b4d-4e7f-9a2c-5d1e0f3b6a74","product_id":"a7b3c9d2-1e4f-4b6a-8c5d-2f9e0a1b3c42","name":"Pro plan","quantity":1,"unit_price_minor":4900,"currency":"GBP","recurring":{"interval":"month","interval_count":1},"billing_max_cycles":null,"trial_period_days":14,"start_rule":"immediate","billing_anchor_rule":"same_day","billing_anchor_day":null,"due_today_minor":0,"first_charge_minor":4900,"first_renewal_at":"2026-05-02T09:00:00.000Z","effective_start_rule":"immediate","metadata":null}],"metadata":null,"expires_at":"2026-04-18T10:05:00.000Z","created_at":"2026-04-18T09:05:00.000Z","updated_at":"2026-04-18T09:05:00.000Z"}}}},"CartCheckoutSession":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"object":{"type":"string","enum":["checkout_session"]},"merchant_id":{"type":"string","format":"uuid"},"customer_id":{"type":["string","null"],"format":"uuid"},"session_token":{"type":["string","null"]},"checkout_url":{"type":["string","null"],"format":"uri","description":"Hosted checkout URL for this session, built server-side from the configured RevKeen checkout origin. Redirect the customer here. Null when the session has no token.","example":"https://pay.revkeen.com/p/rvk_cs_abc123"},"status":{"type":"string","example":"pending"},"mode":{"type":["string","null"]},"amount_minor":{"type":["integer","null"]},"currency":{"type":["string","null"]},"line_items":{"type":"array","items":{"$ref":"#/components/schemas/CartLineItem"}},"metadata":{"type":["object","null"],"additionalProperties":{}},"expires_at":{"type":["string","null"],"format":"date-time"},"created_at":{"type":["string","null"],"format":"date-time"},"updated_at":{"type":["string","null"],"format":"date-time"}},"required":["id","object","merchant_id","customer_id","session_token","status","mode","amount_minor","currency","line_items","metadata","expires_at","created_at","updated_at"],"example":{"id":"9d0e1f2a-3b4c-4d65-9f6a-7b8c9d0e1f64","object":"checkout_session","merchant_id":"8f14e45f-ceea-4e7a-9b4b-7c3f1a2d5e60","customer_id":"c2a1e7d4-5b3f-4a8e-9d21-6f0b3e4c7a11","session_token":"rvk_cs_example_8m3x","checkout_url":"https://pay.revkeen.com/p/rvk_cs_example_8m3x","status":"pending","mode":"subscription","amount_minor":4900,"currency":"GBP","line_items":[{"id":"cli_3f6c2a1e-8b4d-4e7f-9a2c-5d1e0f3b6a74","product_id":"a7b3c9d2-1e4f-4b6a-8c5d-2f9e0a1b3c42","name":"Pro plan","quantity":1,"unit_price_minor":4900,"currency":"GBP","recurring":{"interval":"month","interval_count":1},"billing_max_cycles":null,"trial_period_days":14,"start_rule":"immediate","billing_anchor_rule":"same_day","billing_anchor_day":null,"due_today_minor":0,"first_charge_minor":4900,"first_renewal_at":"2026-05-02T09:00:00.000Z","effective_start_rule":"immediate","metadata":null}],"metadata":null,"expires_at":"2026-04-18T10:05:00.000Z","created_at":"2026-04-18T09:05:00.000Z","updated_at":"2026-04-18T09:05:00.000Z"}},"StorefrontProductListResponse":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/StorefrontProduct"}}},"required":["data"]},"StorefrontProduct":{"type":"object","properties":{"id":{"type":"string"},"object":{"type":"string","enum":["product"]},"product_id":{"type":["string","null"]},"slug":{"type":["string","null"]},"name":{"type":"string"},"description":{"type":["string","null"]},"kind":{"type":"string"},"pricing_model":{"type":"string"},"currency":{"type":"string"},"image_url":{"type":["string","null"]},"default_price_id":{"type":["string","null"]},"prices":{"type":"array","items":{"$ref":"#/components/schemas/StorefrontPrice"}},"trial_days":{"type":"integer"},"usage_meter_id":{"type":["string","null"]},"tax_behavior":{"type":["string","null"]},"tax_code":{"type":["string","null"]},"availability":{"$ref":"#/components/schemas/StorefrontAvailability"},"tags":{"type":"array","items":{"type":"string"},"description":"Merchant tags for store filters and search."},"images":{"type":"array","items":{"$ref":"#/components/schemas/StorefrontProductImage"},"description":"Extra images in display order. image_url stays the main image."},"seo_title":{"type":["string","null"],"description":"Page title for search and social. Null means use the name."},"seo_description":{"type":["string","null"],"description":"Meta description. Null means use the description."},"created_at":{"type":["string","null"],"format":"date-time","description":"When the product was created (ISO 8601). Null when unknown. A store can use it to show a New badge."}},"required":["id","object","product_id","slug","name","description","kind","pricing_model","currency","image_url","default_price_id","prices","trial_days","usage_meter_id","tax_behavior","tax_code","availability","tags","images","seo_title","seo_description"],"description":"Browser-safe product projection for headless storefronts: display data, active prices, and derived availability only.","example":{"id":"a7b3c9d2-1e4f-4b6a-8c5d-2f9e0a1b3c42","object":"product","product_id":"PRO-MONTHLY","slug":"pro-plan","name":"Pro plan","description":"Unlimited invoices, automated reminders and priority support.","kind":"subscription","pricing_model":"recurring","currency":"GBP","image_url":null,"default_price_id":"b5d8e2f1-3a6c-4d9b-8e7f-1c4a2b5d6e73","prices":[{"id":"b5d8e2f1-3a6c-4d9b-8e7f-1c4a2b5d6e73","product_id":"a7b3c9d2-1e4f-4b6a-8c5d-2f9e0a1b3c42","currency":"gbp","unit_amount":4900,"unit_amount_decimal":"4900","type":"recurring","interval":"month","interval_count":1,"billing_scheme":"per_unit","usage_type":"licensed","package_size":null,"trial_period_days":14,"option_values":null,"sku":null,"image_url":null,"name":"Pro"}],"trial_days":14,"usage_meter_id":null,"tax_behavior":"exclusive","tax_code":null,"availability":{"status":"unknown","remaining":null,"display_mode":"state","low_stock_threshold":null},"tags":["saas","monthly"],"images":[],"seo_title":null,"seo_description":null,"created_at":"2026-09-02T09:30:00.000Z"}},"StorefrontPrice":{"type":"object","properties":{"id":{"type":"string"},"product_id":{"type":"string"},"currency":{"type":"string"},"unit_amount":{"type":["integer","null"]},"unit_amount_decimal":{"type":["string","null"]},"type":{"type":["string","null"]},"interval":{"type":["string","null"]},"interval_count":{"type":["integer","null"]},"billing_scheme":{"type":"string"},"usage_type":{"type":["string","null"]},"package_size":{"type":["integer","null"]},"trial_period_days":{"type":["integer","null"]},"option_values":{"type":["object","null"],"additionalProperties":{"type":"string"},"description":"The variant's options, e.g. {\"Size\":\"M\"}. Null when the price is not a variant."},"sku":{"type":["string","null"],"description":"The variant's SKU. Null when not set."},"image_url":{"type":["string","null"],"description":"The variant's image as a browser URL. Null when not set."},"name":{"type":["string","null"],"description":"The price's customer-facing name as the merchant set it, for example Pro. Null when not set; label the price by product name and interval instead."}},"required":["id","product_id","currency","unit_amount","unit_amount_decimal","type","interval","interval_count","billing_scheme","usage_type","package_size","trial_period_days"],"description":"Active price summary for storefront display. Amounts are minor units.","example":{"id":"b5d8e2f1-3a6c-4d9b-8e7f-1c4a2b5d6e73","product_id":"a7b3c9d2-1e4f-4b6a-8c5d-2f9e0a1b3c42","currency":"gbp","unit_amount":4900,"unit_amount_decimal":"4900","type":"recurring","interval":"month","interval_count":1,"billing_scheme":"per_unit","usage_type":"licensed","package_size":null,"trial_period_days":14,"option_values":null,"sku":null,"image_url":null,"name":"Pro"}},"StorefrontAvailability":{"type":"object","properties":{"status":{"type":"string","enum":["unknown","plenty","low","soldout"]},"remaining":{"type":["integer","null"]},"display_mode":{"type":"string"},"low_stock_threshold":{"type":["integer","null"]}},"required":["status","remaining","display_mode","low_stock_threshold"],"description":"Derived availability (capacity + oversell − reserved − confirmed). `unknown` when the product does not track availability."},"StorefrontProductImage":{"type":"object","properties":{"url":{"type":"string"},"alt":{"type":["string","null"]},"source":{"type":"string"},"photographer":{"type":["string","null"]},"attribution_url":{"type":["string","null"]}},"required":["url","alt","source","photographer","attribution_url"],"description":"An extra product image as a browser URL. Show the photographer credit and link when present (required for Pexels images)."},"StorefrontProductResponse":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/StorefrontProduct"}},"required":["data"]},"StorefrontProductError":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error"],"description":"`error` is a string here, not the `{ type, code, message }` object most endpoints return. It holds the message, for example `Product not found`."},"StorefrontOriginListResponse":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/StorefrontOrigin"}}},"required":["data"]},"StorefrontOrigin":{"type":"object","properties":{"id":{"type":"string"},"object":{"type":"string","enum":["storefront_origin"]},"origin":{"type":"string"},"created_at":{"type":"string","format":"date-time"}},"required":["id","object","origin","created_at"],"description":"A registered storefront origin allowed for publishable-key browser calls.","example":{"id":"sfo_9k2m4x7q","object":"storefront_origin","origin":"https://shop.example.com","created_at":"2026-04-10T09:00:00.000Z"}},"StorefrontOriginCreateResponse":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/StorefrontOrigin"}},"required":["data"]},"StorefrontOriginError":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error"],"description":"`error` is a string here, not the `{ type, code, message }` object most endpoints return. It holds a machine-readable code (`INVALID_ORIGIN`, `ORIGIN_EXISTS` or `NOT_FOUND`); `message`, when present, is the human-readable text."},"StorefrontOriginCreateRequest":{"type":"object","properties":{"origin":{"type":"string","description":"Exact origin, e.g. https://shop.example.com or http://localhost:3000.","example":"https://shop.example.com"}},"required":["origin"]},"StorefrontOriginDeleteResponse":{"type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string"},"deleted":{"type":"boolean","enum":[true]}},"required":["id","deleted"]}},"required":["data"]},"StorefrontStatusResponse":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/StorefrontStatus"}},"required":["data"]},"StorefrontStatus":{"type":"object","properties":{"object":{"type":"string","enum":["storefront_status"]},"ready":{"type":"boolean"},"checks":{"type":"array","items":{"$ref":"#/components/schemas/StorefrontStatusCheck"}},"cart":{"type":"object","properties":{"enabled":{"type":"boolean"},"profile":{"type":"string"}},"required":["enabled","profile"]},"keys":{"type":"object","properties":{"publishable":{"$ref":"#/components/schemas/StorefrontKeyStatus"},"secret":{"$ref":"#/components/schemas/StorefrontKeyStatus"}},"required":["publishable","secret"]},"origins":{"type":"object","properties":{"count":{"type":"integer"},"origins":{"type":"array","items":{"type":"string"}}},"required":["count","origins"]},"product_read":{"type":"object","properties":{"active_products":{"type":"integer"},"priced_products":{"type":"integer"},"ready":{"type":"boolean"}},"required":["active_products","priced_products","ready"]},"webhooks":{"type":"object","properties":{"active_endpoints":{"type":"integer"},"unreachable_endpoints":{"type":"integer"}},"required":["active_endpoints","unreachable_endpoints"]},"availability":{"type":"object","properties":{"tracked_products":{"type":"integer"},"mode":{"type":"string","enum":["enabled","not_configured"]}},"required":["tracked_products","mode"]}},"required":["object","ready","checks","cart","keys","origins","product_read","webhooks","availability"],"example":{"object":"storefront_status","ready":true,"checks":[{"id":"cart_enabled","status":"pass","message":"Cart is enabled for this merchant."},{"id":"keys","status":"pass","message":"Publishable and secret Cart keys are provisioned."},{"id":"origins","status":"pass","message":"1 storefront origin registered."},{"id":"product_read","status":"pass","message":"1 active product with an active price."},{"id":"webhooks","status":"pass","message":"1 active webhook endpoint healthy."},{"id":"availability","status":"pass","message":"No products track availability (optional — untracked products sell without capacity limits)."}],"cart":{"enabled":true,"profile":"commerce"},"keys":{"publishable":{"present":true,"active":true,"created_at":"2026-04-10T09:00:00.000Z","last_used_at":"2026-04-18T09:00:00.000Z"},"secret":{"present":true,"active":true,"created_at":"2026-04-10T09:00:00.000Z","last_used_at":"2026-04-18T09:05:00.000Z"}},"origins":{"count":1,"origins":["https://shop.example.com"]},"product_read":{"active_products":1,"priced_products":1,"ready":true},"webhooks":{"active_endpoints":1,"unreachable_endpoints":0},"availability":{"tracked_products":0,"mode":"not_configured"}}},"StorefrontStatusCheck":{"type":"object","properties":{"id":{"type":"string","enum":["cart_enabled","keys","origins","product_read","webhooks","availability"]},"status":{"type":"string","enum":["pass","warn","fail"]},"code":{"type":"string","enum":["CART_DISABLED","KEYS_MISSING","ORIGIN_MISSING","PRODUCT_READ_UNAVAILABLE","WEBHOOK_MISSING","WEBHOOK_UNREACHABLE"]},"message":{"type":"string"},"next_action":{"type":"string"}},"required":["id","status","message"]},"StorefrontKeyStatus":{"type":"object","properties":{"present":{"type":"boolean"},"active":{"type":"boolean"},"created_at":{"type":["string","null"],"format":"date-time"},"last_used_at":{"type":["string","null"],"format":"date-time"}},"required":["present","active","created_at","last_used_at"],"description":"Managed Cart key presence. Key material is never returned."},"Business":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Unique identifier (UUID)"},"object":{"type":"string","enum":["business"],"description":"Object type"},"customer_id":{"type":["string","null"],"format":"uuid","description":"Associated customer ID"},"name":{"type":"string","description":"Business name"},"company_number":{"type":["string","null"],"description":"Company registration number"},"tax_identifier":{"type":["string","null"],"description":"Tax ID (VAT, EIN, etc.)"},"billing_email":{"type":["string","null"],"description":"Billing email address"},"billing_phone":{"type":["string","null"],"description":"Billing phone number"},"billing_address_line1":{"type":["string","null"],"description":"Billing address line 1"},"billing_address_line2":{"type":["string","null"],"description":"Billing address line 2"},"billing_city":{"type":["string","null"],"description":"Billing city"},"billing_postcode":{"type":["string","null"],"description":"Billing postcode/ZIP"},"billing_country":{"type":["string","null"],"description":"Billing country (ISO 3166-1 alpha-2)"},"metadata":{"type":"object","additionalProperties":true,"description":"Custom key-value metadata"},"created_at":{"type":"string","format":"date-time","description":"Creation timestamp (ISO 8601)"},"updated_at":{"type":"string","format":"date-time","description":"Last update timestamp (ISO 8601)"}},"required":["id","object","name","created_at","updated_at"],"description":"A business entity (company) associated with a customer for B2B billing."},"CustomerStateResponse":{"type":"object","properties":{"customer":{"$ref":"#/components/schemas/CustomerStateCustomer"},"subscriptions":{"type":"object","properties":{"active":{"type":"array","items":{"$ref":"#/components/schemas/CustomerStateSubscription"},"description":"Active, trialing and past-due subscriptions, newest first, read on their own (up to 50)."},"active_has_more":{"type":"boolean","description":"True when the customer has more active subscriptions than `active` holds."},"all":{"type":"array","items":{"$ref":"#/components/schemas/CustomerStateSubscription"},"description":"The newest 20 subscriptions in any state."},"all_has_more":{"type":"boolean","description":"True when the customer has more subscriptions than `all` holds."}},"required":["active","active_has_more","all","all_has_more"]},"invoices":{"type":"object","properties":{"overdue":{"type":"array","items":{"$ref":"#/components/schemas/CustomerStateInvoice"},"description":"Overdue invoices, newest first, read on their own (up to 50). Overdue means unsettled and past due, in dunning, or marked overdue."},"overdue_has_more":{"type":"boolean","description":"True when the customer has more overdue invoices than `overdue` holds."},"recent":{"type":"array","items":{"$ref":"#/components/schemas/CustomerStateInvoice"},"description":"The newest 10 invoices in any state."},"recent_has_more":{"type":"boolean","description":"True when the customer has more invoices than `recent` holds."}},"required":["overdue","overdue_has_more","recent","recent_has_more"]}},"required":["customer","subscriptions","invoices"],"description":"One-call customer snapshot: identity, subscriptions, and invoices. Each list says whether it was cut short."},"CustomerStateCustomer":{"type":"object","properties":{"id":{"type":"string","description":"Customer id"},"email":{"type":["string","null"]},"name":{"type":["string","null"]},"company_name":{"type":["string","null"]},"created_at":{"type":["string","null"],"format":"date-time"}},"required":["id","email","name","company_name","created_at"],"description":"Who the customer is."},"CustomerStateSubscription":{"type":"object","properties":{"id":{"type":"string"},"status":{"type":["string","null"]},"amount_minor":{"type":["integer","null"],"description":"Recurring amount in minor units"},"currency":{"type":["string","null"]},"current_period_end":{"type":["string","null"],"format":"date-time"},"product_id":{"type":["string","null"]},"price_id":{"type":["string","null"]}},"required":["id","status","amount_minor","currency","current_period_end","product_id","price_id"],"description":"One subscription of the customer."},"CustomerStateInvoice":{"type":"object","properties":{"id":{"type":"string"},"status":{"type":["string","null"]},"amount_due_minor":{"type":["integer","null"],"description":"Amount still due in minor units. Null when the invoice records none; never the invoice total."},"currency":{"type":["string","null"]},"due_date":{"type":["string","null"],"format":"date-time"},"overdue":{"type":"boolean"}},"required":["id","status","amount_due_minor","currency","due_date","overdue"],"description":"One invoice of the customer."},"CustomerInvoice":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"customer_id":{"type":"string","format":"uuid"},"invoice_number":{"type":["string","null"]},"status":{"type":"string"},"total_minor":{"type":"integer"},"currency":{"type":"string"},"due_date":{"type":["string","null"]},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"}},"required":["id","customer_id","invoice_number","status","total_minor","currency","due_date","created_at","updated_at"],"description":"An invoice belonging to a specific customer, with summarized billing details."},"ErrorResponse":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}},"required":["code","message"]}},"required":["error"],"description":"Standard error response for customer sub-resource operations."},"CustomerSubscription":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"customer_id":{"type":"string","format":"uuid"},"price_id":{"type":["string","null"],"format":"uuid"},"status":{"type":"string"},"current_period_start":{"type":["string","null"]},"current_period_end":{"type":["string","null"]},"cancel_at_period_end":{"type":"boolean"},"canceled_at":{"type":["string","null"],"format":"date-time"},"trial_end":{"type":["string","null"]},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"}},"required":["id","customer_id","price_id","status","current_period_start","current_period_end","cancel_at_period_end","canceled_at","trial_end","created_at","updated_at"],"description":"A subscription belonging to a specific customer, with plan and status details."},"CustomerOrder":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"public_id":{"type":"string"},"customer_id":{"type":"string","format":"uuid"},"status":{"type":"string"},"total_minor":{"type":"integer"},"currency":{"type":"string"},"fulfilled_at":{"type":["string","null"],"format":"date-time"},"canceled_at":{"type":["string","null"],"format":"date-time"},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"}},"required":["id","public_id","customer_id","status","total_minor","currency","fulfilled_at","canceled_at","created_at","updated_at"],"description":"An order belonging to a specific customer, with line items and fulfillment status."},"CustomerPayment":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"customer_id":{"type":["string","null"],"format":"uuid"},"amount_minor":{"type":"integer"},"currency":{"type":"string"},"status":{"type":"string"},"payment_method_type":{"type":["string","null"]},"gateway_transaction_id":{"type":["string","null"]},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"}},"required":["id","customer_id","amount_minor","currency","status","payment_method_type","gateway_transaction_id","created_at","updated_at"],"description":"A payment transaction belonging to a specific customer, with amount, status, and gateway details."},"CustomerCreateResponse":{"type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Unique customer identifier","example":"c2a1e7d4-5b3f-4a8e-9d21-6f0b3e4c7a11"},"merchant_id":{"type":"string","format":"uuid","description":"ID of the merchant this customer belongs to","example":"8f14e45f-ceea-4e7a-9b4b-7c3f1a2d5e60"},"merchant_ref_id":{"type":"string","description":"Merchant-assigned reference ID for external system mapping"},"external_id":{"type":"string","description":"Your system's identifier for this customer, set with external_id on create or update. Unique per merchant and source: no two customers whose external id you set through the API can share one, but a customer synced from an integration can carry the same value, and that integration's identifier is what such a customer shows here. Send it as external_customer_id when ingesting usage events, or look customers up with GET /v2/customers?external_id=.","example":"usr_123"},"email":{"type":"string","format":"email","description":"Customer's email address","example":"ada.lovelace@example.com"},"name":{"type":"string","description":"Customer's full name"},"phone":{"type":"string","description":"Customer's phone number"},"company_name":{"type":"string","description":"Customer's company name"},"address_line1":{"type":"string","description":"Billing address line 1"},"address_line2":{"type":"string","description":"Billing address line 2"},"city":{"type":"string","description":"Billing address city"},"state":{"type":"string","description":"Billing address state or county"},"postal_code":{"type":"string","description":"Billing address postal code"},"country":{"type":"string","description":"Billing address country code"},"auth_user_id":{"type":"string","format":"uuid","description":"Linked Better Auth user ID for portal access"},"metadata":{"type":"object","additionalProperties":true,"description":"Arbitrary key-value metadata attached to this customer"},"custom_fields":{"type":"object","additionalProperties":true,"description":"Merchant-defined custom fields displayed as columns in the dashboard"},"created_at":{"type":"string","format":"date-time","description":"When the customer was created (ISO 8601)"},"updated_at":{"type":"string","format":"date-time","description":"When the customer was last updated (ISO 8601)"}},"required":["id","merchant_id","email","created_at","updated_at"],"example":{"id":"c2a1e7d4-5b3f-4a8e-9d21-6f0b3e4c7a11","merchant_id":"8f14e45f-ceea-4e7a-9b4b-7c3f1a2d5e60","merchant_ref_id":"CRM-10482","external_id":"usr_123","email":"ada.lovelace@example.com","name":"Ada Lovelace","phone":"+44 20 7946 0958","company_name":"Analytical Engines Ltd","address_line1":"1 Example Street","address_line2":"Suite 4","city":"London","state":"Greater London","postal_code":"EC1A 1BB","country":"GB","auth_user_id":"00000000-0000-4000-8000-000000000001","metadata":{"crm_id":"10482"},"custom_fields":{"membership_tier":"gold"},"created_at":"2026-04-02T10:00:00.000Z","updated_at":"2026-04-02T10:05:00.000Z"}}},"required":["data"],"description":"The newly created customer object."},"BodyValidationError":{"type":"object","properties":{"error":{"type":"object","properties":{"type":{"type":"string","enum":["invalid_request_error"]},"code":{"type":"string","enum":["parameter_invalid","validation_error"]},"message":{"type":"string"},"request_id":{"type":"string"},"param":{"type":"string","description":"The body field that was rejected, as a dotted path.","example":"email"}},"required":["type","code","message"]}},"required":["error"],"description":"Returned when the request body fails validation. `param` names the field to fix."},"CustomerListResponse":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Unique customer identifier","example":"c2a1e7d4-5b3f-4a8e-9d21-6f0b3e4c7a11"},"merchant_id":{"type":"string","format":"uuid","description":"ID of the merchant this customer belongs to","example":"8f14e45f-ceea-4e7a-9b4b-7c3f1a2d5e60"},"merchant_ref_id":{"type":"string","description":"Merchant-assigned reference ID for external system mapping"},"external_id":{"type":"string","description":"Your system's identifier for this customer, set with external_id on create or update. Unique per merchant and source: no two customers whose external id you set through the API can share one, but a customer synced from an integration can carry the same value, and that integration's identifier is what such a customer shows here. Send it as external_customer_id when ingesting usage events, or look customers up with GET /v2/customers?external_id=.","example":"usr_123"},"email":{"type":"string","format":"email","description":"Customer's email address","example":"ada.lovelace@example.com"},"name":{"type":"string","description":"Customer's full name"},"phone":{"type":"string","description":"Customer's phone number"},"company_name":{"type":"string","description":"Customer's company name"},"address_line1":{"type":"string","description":"Billing address line 1"},"address_line2":{"type":"string","description":"Billing address line 2"},"city":{"type":"string","description":"Billing address city"},"state":{"type":"string","description":"Billing address state or county"},"postal_code":{"type":"string","description":"Billing address postal code"},"country":{"type":"string","description":"Billing address country code"},"auth_user_id":{"type":"string","format":"uuid","description":"Linked Better Auth user ID for portal access"},"metadata":{"type":"object","additionalProperties":true,"description":"Arbitrary key-value metadata attached to this customer"},"custom_fields":{"type":"object","additionalProperties":true,"description":"Merchant-defined custom fields displayed as columns in the dashboard"},"created_at":{"type":"string","format":"date-time","description":"When the customer was created (ISO 8601)"},"updated_at":{"type":"string","format":"date-time","description":"When the customer was last updated (ISO 8601)"}},"required":["id","merchant_id","email","created_at","updated_at"],"example":{"id":"c2a1e7d4-5b3f-4a8e-9d21-6f0b3e4c7a11","merchant_id":"8f14e45f-ceea-4e7a-9b4b-7c3f1a2d5e60","merchant_ref_id":"CRM-10482","external_id":"usr_123","email":"ada.lovelace@example.com","name":"Ada Lovelace","phone":"+44 20 7946 0958","company_name":"Analytical Engines Ltd","address_line1":"1 Example Street","address_line2":"Suite 4","city":"London","state":"Greater London","postal_code":"EC1A 1BB","country":"GB","auth_user_id":"00000000-0000-4000-8000-000000000001","metadata":{"crm_id":"10482"},"custom_fields":{"membership_tier":"gold"},"created_at":"2026-04-02T10:00:00.000Z","updated_at":"2026-04-02T10:05:00.000Z"}}},"pagination":{"type":"object","properties":{"limit":{"type":"number","example":20},"offset":{"type":"number","example":0},"total":{"type":"number","example":42}},"required":["limit","offset"]}},"required":["data","pagination"],"description":"A paginated list of customers matching the query filters."},"CustomerRetrieveResponse":{"type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Unique customer identifier","example":"c2a1e7d4-5b3f-4a8e-9d21-6f0b3e4c7a11"},"merchant_id":{"type":"string","format":"uuid","description":"ID of the merchant this customer belongs to","example":"8f14e45f-ceea-4e7a-9b4b-7c3f1a2d5e60"},"merchant_ref_id":{"type":"string","description":"Merchant-assigned reference ID for external system mapping"},"external_id":{"type":"string","description":"Your system's identifier for this customer, set with external_id on create or update. Unique per merchant and source: no two customers whose external id you set through the API can share one, but a customer synced from an integration can carry the same value, and that integration's identifier is what such a customer shows here. Send it as external_customer_id when ingesting usage events, or look customers up with GET /v2/customers?external_id=.","example":"usr_123"},"email":{"type":"string","format":"email","description":"Customer's email address","example":"ada.lovelace@example.com"},"name":{"type":"string","description":"Customer's full name"},"phone":{"type":"string","description":"Customer's phone number"},"company_name":{"type":"string","description":"Customer's company name"},"address_line1":{"type":"string","description":"Billing address line 1"},"address_line2":{"type":"string","description":"Billing address line 2"},"city":{"type":"string","description":"Billing address city"},"state":{"type":"string","description":"Billing address state or county"},"postal_code":{"type":"string","description":"Billing address postal code"},"country":{"type":"string","description":"Billing address country code"},"auth_user_id":{"type":"string","format":"uuid","description":"Linked Better Auth user ID for portal access"},"metadata":{"type":"object","additionalProperties":true,"description":"Arbitrary key-value metadata attached to this customer"},"custom_fields":{"type":"object","additionalProperties":true,"description":"Merchant-defined custom fields displayed as columns in the dashboard"},"created_at":{"type":"string","format":"date-time","description":"When the customer was created (ISO 8601)"},"updated_at":{"type":"string","format":"date-time","description":"When the customer was last updated (ISO 8601)"}},"required":["id","merchant_id","email","created_at","updated_at"],"example":{"id":"c2a1e7d4-5b3f-4a8e-9d21-6f0b3e4c7a11","merchant_id":"8f14e45f-ceea-4e7a-9b4b-7c3f1a2d5e60","merchant_ref_id":"CRM-10482","external_id":"usr_123","email":"ada.lovelace@example.com","name":"Ada Lovelace","phone":"+44 20 7946 0958","company_name":"Analytical Engines Ltd","address_line1":"1 Example Street","address_line2":"Suite 4","city":"London","state":"Greater London","postal_code":"EC1A 1BB","country":"GB","auth_user_id":"00000000-0000-4000-8000-000000000001","metadata":{"crm_id":"10482"},"custom_fields":{"membership_tier":"gold"},"created_at":"2026-04-02T10:00:00.000Z","updated_at":"2026-04-02T10:05:00.000Z"}}},"required":["data"],"description":"A single customer object with full details."},"CustomerUpdateResponse":{"type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Unique customer identifier","example":"c2a1e7d4-5b3f-4a8e-9d21-6f0b3e4c7a11"},"merchant_id":{"type":"string","format":"uuid","description":"ID of the merchant this customer belongs to","example":"8f14e45f-ceea-4e7a-9b4b-7c3f1a2d5e60"},"merchant_ref_id":{"type":"string","description":"Merchant-assigned reference ID for external system mapping"},"external_id":{"type":"string","description":"Your system's identifier for this customer, set with external_id on create or update. Unique per merchant and source: no two customers whose external id you set through the API can share one, but a customer synced from an integration can carry the same value, and that integration's identifier is what such a customer shows here. Send it as external_customer_id when ingesting usage events, or look customers up with GET /v2/customers?external_id=.","example":"usr_123"},"email":{"type":"string","format":"email","description":"Customer's email address","example":"ada.lovelace@example.com"},"name":{"type":"string","description":"Customer's full name"},"phone":{"type":"string","description":"Customer's phone number"},"company_name":{"type":"string","description":"Customer's company name"},"address_line1":{"type":"string","description":"Billing address line 1"},"address_line2":{"type":"string","description":"Billing address line 2"},"city":{"type":"string","description":"Billing address city"},"state":{"type":"string","description":"Billing address state or county"},"postal_code":{"type":"string","description":"Billing address postal code"},"country":{"type":"string","description":"Billing address country code"},"auth_user_id":{"type":"string","format":"uuid","description":"Linked Better Auth user ID for portal access"},"metadata":{"type":"object","additionalProperties":true,"description":"Arbitrary key-value metadata attached to this customer"},"custom_fields":{"type":"object","additionalProperties":true,"description":"Merchant-defined custom fields displayed as columns in the dashboard"},"created_at":{"type":"string","format":"date-time","description":"When the customer was created (ISO 8601)"},"updated_at":{"type":"string","format":"date-time","description":"When the customer was last updated (ISO 8601)"}},"required":["id","merchant_id","email","created_at","updated_at"],"example":{"id":"c2a1e7d4-5b3f-4a8e-9d21-6f0b3e4c7a11","merchant_id":"8f14e45f-ceea-4e7a-9b4b-7c3f1a2d5e60","merchant_ref_id":"CRM-10482","external_id":"usr_123","email":"ada.lovelace@example.com","name":"Ada Lovelace","phone":"+44 20 7946 0958","company_name":"Analytical Engines Ltd","address_line1":"1 Example Street","address_line2":"Suite 4","city":"London","state":"Greater London","postal_code":"EC1A 1BB","country":"GB","auth_user_id":"00000000-0000-4000-8000-000000000001","metadata":{"crm_id":"10482"},"custom_fields":{"membership_tier":"gold"},"created_at":"2026-04-02T10:00:00.000Z","updated_at":"2026-04-02T10:05:00.000Z"}}},"required":["data"],"description":"The updated customer object reflecting the changes."},"CustomerPaymentMethodsListResponse":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/PaymentMethod"}}},"required":["data"],"description":"A list of payment methods attached to a customer."},"PaymentMethod":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Unique identifier for the payment method","example":"550e8400-e29b-41d4-a716-446655440000"},"object":{"type":"string","enum":["payment_method"],"description":"Object type, always 'payment_method'"},"public_id":{"type":["string","null"],"description":"Public ID visible in API responses (pm_xxx format)","example":"pm_1a2b3c4d5e6f"},"type":{"type":"string","enum":["card","bank_account","us_bank_account","bacs_direct_debit"],"description":"The type of payment method","example":"card"},"status":{"type":"string","enum":["active","inactive","expired"],"description":"The status of the payment method","example":"active"},"customer_id":{"type":"string","format":"uuid","description":"ID of the customer this payment method belongs to"},"is_default":{"type":"boolean","description":"Whether this is the customer's default payment method"},"card":{"$ref":"#/components/schemas/CardDetails"},"us_bank_account":{"$ref":"#/components/schemas/BankAccountDetails"},"bacs_direct_debit":{"$ref":"#/components/schemas/BacsDirectDebitDetails"},"billing_details":{"$ref":"#/components/schemas/BillingDetails"},"metadata":{"type":"object","additionalProperties":{},"description":"Custom metadata attached to the payment method"},"created_at":{"type":"string","format":"date-time","description":"When the payment method was created"},"updated_at":{"type":"string","format":"date-time","description":"When the payment method was last updated"}},"required":["id","object","public_id","type","status","customer_id","is_default","card","us_bank_account","bacs_direct_debit","billing_details","metadata","created_at","updated_at"],"description":"A saved payment instrument (card, bank account, etc.) attached to a customer for future charges."},"CardDetails":{"type":["object","null"],"properties":{"brand":{"type":["string","null"],"enum":["visa","mastercard","amex","discover","diners","jcb","unionpay","unknown",null],"description":"The card brand","example":"visa"},"last4":{"type":["string","null"],"minLength":4,"maxLength":4,"description":"The last 4 digits of the card number","example":"4242"},"exp_month":{"type":["integer","null"],"minimum":1,"maximum":12,"description":"Expiration month (1-12)","example":12},"exp_year":{"type":["integer","null"],"description":"Expiration year","example":2028},"funding":{"type":["string","null"],"enum":["credit","debit","prepaid","unknown",null],"description":"The card funding type","example":"credit"}},"required":["brand","last4","exp_month","exp_year","funding"],"description":"Card details (if type is 'card')"},"BankAccountDetails":{"type":["object","null"],"properties":{"bank_name":{"type":["string","null"],"description":"The name of the bank","example":"Chase"},"last4":{"type":["string","null"],"minLength":4,"maxLength":4,"description":"The last 4 digits of the account number","example":"6789"},"routing_number_last4":{"type":["string","null"],"minLength":4,"maxLength":4,"description":"The last 4 digits of the routing number","example":"1234"},"account_type":{"type":["string","null"],"enum":["checking","savings",null],"description":"The type of bank account","example":"checking"}},"required":["bank_name","last4","routing_number_last4","account_type"],"description":"Bank account details (if type is 'us_bank_account')"},"BacsDirectDebitDetails":{"type":["object","null"],"properties":{"sort_code":{"type":["string","null"],"description":"Masked sort code — never the full value","example":"XX-XX-**"},"account_number_last4":{"type":["string","null"],"description":"Last 4 digits of the account number","example":"1234"},"bank_name":{"type":["string","null"],"description":"Resolved bank name","example":"Barclays"},"mandate_id":{"type":"string","description":"The Direct Debit mandate backing this payment method"},"mandate_ref":{"type":["string","null"],"description":"RevKeen mandate reference (RK-XXXXXX)"},"mandate_status":{"type":"string","description":"Mandate status","example":"active"}},"required":["sort_code","account_number_last4","bank_name","mandate_id","mandate_ref","mandate_status"],"description":"Bacs Direct Debit details (if type is 'bacs_direct_debit')"},"BillingDetails":{"type":["object","null"],"properties":{"name":{"type":["string","null"],"description":"Full name on the payment method"},"email":{"type":["string","null"],"format":"email","description":"Email address"},"phone":{"type":["string","null"],"description":"Phone number"},"address":{"type":["object","null"],"properties":{"line1":{"type":["string","null"]},"line2":{"type":["string","null"]},"city":{"type":["string","null"]},"state":{"type":["string","null"]},"postal_code":{"type":["string","null"]},"country":{"type":["string","null"]}},"required":["line1","line2","city","state","postal_code","country"],"description":"Billing address"}},"required":["name","email","phone","address"],"description":"Billing details associated with the payment method"},"CustomerPortalSessionUrlCreateResponse":{"type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"customer_id":{"type":"string","format":"uuid"},"merchant_id":{"type":"string","format":"uuid"},"url":{"type":"string","format":"uri"},"expires_at":{"type":"string","format":"date-time"},"created_at":{"type":"string","format":"date-time"}},"required":["id","customer_id","merchant_id","url","expires_at","created_at"]}},"required":["data"],"description":"A portal session URL that redirects the customer to their self-service billing portal."},"SubscriptionCreateResponse":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Subscription"}},"required":["data"],"description":"The newly created subscription with billing schedule and initial invoice details."},"Subscription":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Subscription UUID","example":"d4e6f8a1-2b3c-4d5e-9f60-7a8b9c0d1e24"},"merchant_id":{"type":"string","format":"uuid","description":"Owning merchant UUID","example":"8f14e45f-ceea-4e7a-9b4b-7c3f1a2d5e60"},"customer_id":{"type":"string","format":"uuid","description":"Customer UUID","example":"c2a1e7d4-5b3f-4a8e-9d21-6f0b3e4c7a11"},"product_id":{"type":["string","null"],"format":"uuid","description":"Product UUID","example":"a7b3c9d2-1e4f-4b6a-8c5d-2f9e0a1b3c42"},"price_id":{"type":["string","null"],"format":"uuid","description":"Price UUID","example":"b5d8e2f1-3a6c-4d9b-8e7f-1c4a2b5d6e73"},"plan_id":{"type":["string","null"],"format":"uuid","description":"Plan UUID"},"status":{"type":"string","description":"Subscription status","example":"active"},"quantity":{"type":"integer","description":"Subscribed quantity"},"amount_minor":{"type":"integer","description":"Recurring amount in minor units","example":4900},"currency":{"type":"string","description":"Three-letter ISO currency code","example":"GBP"},"billing_interval":{"type":"string","description":"Billing interval (day, week, month, year)"},"current_period_start":{"type":"string","format":"date-time","description":"Current period start (ISO 8601)"},"current_period_end":{"type":"string","format":"date-time","description":"Current period end (ISO 8601)"},"trial_end":{"type":["string","null"],"format":"date-time","description":"Trial end (ISO 8601)"},"canceled_at":{"type":["string","null"],"format":"date-time","description":"Cancellation timestamp (ISO 8601)"},"created_at":{"type":"string","format":"date-time","description":"Creation timestamp (ISO 8601)"},"updated_at":{"type":"string","format":"date-time","description":"Last update timestamp (ISO 8601)"},"dunning":{"type":["object","null"],"properties":{"is_in_dunning":{"type":"boolean"},"phase":{"type":["number","null"]},"phase_label":{"type":["string","null"]},"phase_severity":{"type":["string","null"],"enum":["info","warning","critical",null]},"retry_count":{"type":"number"},"total_possible_retries":{"type":"number"},"next_retry_at":{"type":["string","null"],"format":"date-time"},"days_in_dunning":{"type":"number"},"access_restricted":{"type":"boolean"}},"required":["is_in_dunning","phase","phase_label","phase_severity","retry_count","total_possible_retries","next_retry_at","days_in_dunning","access_restricted"],"description":"Dunning summary when past due"},"payment_rail":{"type":["string","null"],"description":"How the subscription is collected, for example `card` or `direct_debit`. Null when it follows the payment method."},"mandate_id":{"type":["string","null"],"format":"uuid","description":"The Direct Debit mandate the subscription collects against"},"payment_method_id":{"type":["string","null"],"format":"uuid","description":"The saved payment method the subscription charges"}},"required":["id","merchant_id","customer_id","status","quantity","amount_minor","currency","billing_interval","current_period_start","current_period_end","created_at","updated_at"],"description":"A subscription with full plan, billing, and status details.","example":{"id":"d4e6f8a1-2b3c-4d5e-9f60-7a8b9c0d1e24","merchant_id":"8f14e45f-ceea-4e7a-9b4b-7c3f1a2d5e60","customer_id":"c2a1e7d4-5b3f-4a8e-9d21-6f0b3e4c7a11","product_id":"a7b3c9d2-1e4f-4b6a-8c5d-2f9e0a1b3c42","price_id":"b5d8e2f1-3a6c-4d9b-8e7f-1c4a2b5d6e73","plan_id":null,"status":"active","quantity":1,"amount_minor":4900,"currency":"GBP","billing_interval":"month","current_period_start":"2026-04-16T10:00:00.000Z","current_period_end":"2026-05-16T10:00:00.000Z","trial_end":"2026-04-16T10:00:00.000Z","canceled_at":null,"created_at":"2026-04-02T10:00:00.000Z","updated_at":"2026-04-02T10:05:00.000Z","dunning":null,"payment_rail":"card","mandate_id":null,"payment_method_id":"9e8d7c6b-5a4f-4e3d-8c2b-1a0f9e8d7c65"}},"SubscriptionListResponse":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Subscription"}},"pagination":{"type":"object","properties":{"page":{"type":"number","example":1},"limit":{"type":"number","example":20},"total":{"type":"number","example":42},"totalPages":{"type":"number","example":3}},"required":["page","limit","total","totalPages"]}},"required":["data","pagination"],"description":"A paginated list of subscriptions matching the query filters."},"SubscriptionCustomerNotFound":{"type":"object","properties":{"error":{"type":"object","properties":{"type":{"type":"string","enum":["invalid_request_error"]},"code":{"type":"string","enum":["resource_missing"]},"message":{"type":"string"},"request_id":{"type":"string"}},"required":["type","code","message"]}},"required":["error"],"description":"Returned when `customerId` names no customer of the authenticated merchant."},"SubscriptionRetrieveResponse":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Subscription"}},"required":["data"],"description":"A single subscription object with full plan, billing, and status details."},"SubscriptionUpdateResponse":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Subscription"},"dd_collection_stopped":{"$ref":"#/components/schemas/DdCollectionStopped"}},"required":["data"],"description":"The updated subscription reflecting the changes to metadata or billing parameters."},"DdCollectionStopped":{"type":"object","properties":{"open_invoice_ids":{"type":"array","items":{"type":"string"}},"message":{"type":"string"}},"required":["open_invoice_ids","message"],"description":"Direct Debit collection was stopped. These invoices remain open and will not be collected automatically."},"SubscriptionUpdateInput":{"type":"object","properties":{"metadata":{"type":"object","additionalProperties":{}}},"additionalProperties":false},"SubscriptionChangePlanResponse":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Subscription"},"plan_change":{"$ref":"#/components/schemas/SubscriptionPlanChange"}},"required":["data","plan_change"],"description":"The subscription after the plan change, and the change itself: the same figures the preview returns."},"SubscriptionPlanChange":{"type":"object","properties":{"subscription_id":{"type":"string","format":"uuid","example":"d4e6f8a1-2b3c-4d5e-9f60-7a8b9c0d1e24"},"current_price_id":{"type":"string","description":"The price the subscription is on before the change."},"current_plan_name":{"type":["string","null"],"description":"The current plan's product name."},"new_price_id":{"type":"string","format":"uuid","description":"The price the subscription moves to. A product id in the request resolves to its default price.","example":"b5d8e2f1-3a6c-4d9b-8e7f-1c4a2b5d6e73"},"new_plan_name":{"type":["string","null"],"description":"The new plan's product name."},"new_product_id":{"type":["string","null"],"description":"The product of the new price."},"billing_interval":{"type":["string","null"],"description":"The new price's interval. Always the subscription's own: changing interval is refused."},"billing_interval_count":{"type":["integer","null"],"description":"The new price's interval count."},"quantity":{"type":"integer","description":"The subscribed quantity, which a plan change keeps."},"current_amount_minor":{"type":["integer","null"],"description":"The renewal amount before the change, in minor units.","example":1000},"new_amount_minor":{"type":"integer","description":"The renewal amount after the change: the new unit amount times `quantity`. Always above zero.","example":3000},"proration_behavior":{"type":"string","enum":["next_invoice","none"],"description":"How the difference is billed. `next_invoice` (also what `invoice_immediately` means here) adds it to the next renewal invoice; `none` changes the plan with no proration."},"effective_date":{"type":"string","format":"date-time","description":"When the change takes effect for proration (ISO 8601)."},"proration":{"$ref":"#/components/schemas/SubscriptionPlanChangeProration"},"pending_proration_net_amount_minor":{"type":["integer","null"],"description":"What the next renewal invoice adds (positive) or credits (negative) after this change, including anything already pending from earlier changes. Null when nothing is pending.","example":2000}},"required":["subscription_id","current_price_id","current_plan_name","new_price_id","new_plan_name","new_product_id","billing_interval","billing_interval_count","quantity","current_amount_minor","new_amount_minor","proration_behavior","effective_date","pending_proration_net_amount_minor"],"description":"A plan change: the prices, the renewal amounts before and after, and the proration billed on the next renewal invoice. Nothing is charged when the plan changes."},"SubscriptionPlanChangeProration":{"type":"object","properties":{"credit_amount_minor":{"type":"integer","description":"Credit for the unused time on the current plan, in minor units.","example":1000},"charge_amount_minor":{"type":"integer","description":"Charge for the remaining time on the new plan, in minor units.","example":3000},"net_amount_minor":{"type":"integer","description":"`charge_amount_minor` minus `credit_amount_minor`. Positive is added to the next renewal invoice; negative is credited there.","example":2000},"is_upgrade":{"type":"boolean","description":"Whether the new plan costs more than the current one."},"days_remaining":{"type":"integer","description":"Days left in the current period at the effective date."},"days_in_period":{"type":"integer","description":"Days in the current period."},"currency":{"type":"string","description":"Three-letter ISO currency code of the amounts.","example":"GBP"}},"required":["credit_amount_minor","charge_amount_minor","net_amount_minor","is_upgrade","days_remaining","days_in_period","currency"],"description":"This change's proration. Absent when `proration_behavior` is `none`."},"SubscriptionPlanChangeError":{"type":"object","properties":{"error":{"type":"object","properties":{"type":{"type":"string","enum":["invalid_request_error"]},"code":{"type":"string","enum":["parameter_invalid","invalid_status","resource_missing","price_missing","price_not_recurring","interval_mismatch","currency_mismatch","price_amount_missing","invalid_request","charge_now_card_only"],"description":"`parameter_invalid`: the body failed validation. `invalid_status`: only active, trialing or past_due subscriptions can change plan. `resource_missing`: `newPlanId` names no price or product of yours. `price_missing`: the subscription has no current price. `price_not_recurring`, `interval_mismatch`, `currency_mismatch`: the new price is not a recurring price on the subscription's interval and currency. `price_amount_missing`: the new price has no amount above zero. `charge_now_card_only`: on `RevKeen-Version` `2026-11-02` or later, `prorationBehavior: invoice_immediately` means charge the card now, which only a card subscription can do; send `next_invoice`."},"message":{"type":"string"},"param":{"type":"string"},"request_id":{"type":"string"},"details":{"type":"object","description":"Extra detail for the refusal, for example the two intervals."}},"required":["type","code","message"]}},"required":["error"],"description":"Returned when the plan change cannot be made. Nothing was changed."},"SubscriptionChargeNowInteractiveSessionError":{"type":"object","properties":{"error":{"type":"object","properties":{"type":{"type":"string","enum":["invalid_request_error"]},"code":{"type":"string","enum":["charge_now_requires_interactive_session"]},"message":{"type":"string"},"param":{"type":"string"},"request_id":{"type":"string"}},"required":["type","code","message"]}},"required":["error"],"description":"Returned on `RevKeen-Version` `2026-11-02` or later for `prorationBehavior: invoice_immediately` on a card subscription: charging a card at the moment of a plan change is a dashboard action that needs a signed-in member. Send `next_invoice`. Nothing was changed."},"SubscriptionPlanChangeConflictError":{"type":"object","properties":{"error":{"type":"object","properties":{"type":{"type":"string","enum":["invalid_request_error"]},"code":{"type":"string","enum":["direct_debit_plan_change_unsupported"]},"message":{"type":"string"},"param":{"type":"string"},"request_id":{"type":"string"}},"required":["type","code","message"]}},"required":["error"],"description":"Returned for a Direct Debit subscription: a change of amount needs advance notice to the payer, which this operation does not send. Change the plan from the dashboard. Nothing was changed."},"SubscriptionChangePlanPreviewResponse":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/SubscriptionPlanChange"}},"required":["data"],"description":"The plan change the same request to change-plan would make. Nothing was changed."},"PreviewSubscriptionPlanChangeInput":{"type":"object","properties":{"newPlanId":{"type":"string","format":"uuid","description":"The price to move to. A product id resolves to the product's default price.","example":"b5d8e2f1-3a6c-4d9b-8e7f-1c4a2b5d6e73"},"prorationBehavior":{"type":"string","enum":["invoice_immediately","next_invoice","none"],"description":"As for the change. `next_invoice` (default; `invoice_immediately` is applied the same way on versions up to `2026-05-01`) prorates onto the next renewal invoice; `none` previews a change with no proration."},"effectiveDate":{"type":"string","format":"date-time","description":"Price the proration as of this date (ISO 8601). Defaults to now."}},"required":["newPlanId"],"description":"The plan change to preview. The same fields as the change, without `reason`."},"SubscriptionChangeQuantityResponse":{"type":"object","properties":{"subscription":{"$ref":"#/components/schemas/Subscription"},"proration":{"type":"object","description":"Proration details"},"previousQuantity":{"type":"number"},"invoiceCreated":{"type":"boolean"}},"required":["subscription","invoiceCreated"],"description":"The subscription after a quantity change, with proration details if applicable."},"SubscriptionCancelResponse":{"type":"object","properties":{"success":{"type":"boolean"},"dd_collection_stopped":{"$ref":"#/components/schemas/DdCollectionStopped"}},"required":["success"],"description":"The subscription after cancellation, showing the effective end date and final billing period."},"SubscriptionDeleteConflict":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"CONFLICT"},"message":{"type":"string"},"details":{"type":"object","properties":{"reason":{"type":"string","enum":["unbilled_usage","usage_history"]},"unbilled_usage_record_count":{"type":"integer"},"usage_meter_decision_count":{"type":"integer"}},"required":["reason"]}},"required":["code","message","details"]}},"required":["error"],"description":"The subscription has usage that has not been invoiced, or usage history. Nothing was deleted. Cancel the subscription instead."},"SubscriptionPreviewRenewalResponse":{"type":"object","properties":{"data":{"type":"object","properties":{"subscriptionId":{"type":"string","format":"uuid"},"currentPeriodEnd":{"type":"string","format":"date-time"},"upcomingRenewals":{"type":"array","items":{"type":"object","properties":{"sequence":{"type":"integer","minimum":1},"date":{"type":"string","format":"date-time"},"formattedDate":{"type":"string"},"dayName":{"type":"string"},"amountMinor":{"type":"integer"},"formattedAmount":{"type":"string"},"isTrialEnd":{"type":"boolean"},"isFirstCharge":{"type":"boolean"},"isFinalPayment":{"type":"boolean"}},"required":["sequence","date","formattedDate","dayName","amountMinor","formattedAmount"]}},"nextInvoiceAmountMinor":{"type":"integer"},"currency":{"type":"string","minLength":3,"maxLength":3}},"required":["subscriptionId","currentPeriodEnd","upcomingRenewals","nextInvoiceAmountMinor","currency"]}},"required":["data"],"description":"A preview of the next renewal charge, including line items, amounts, and billing date."},"SubscriptionPauseResponse":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Subscription"},"dd_collection_stopped":{"$ref":"#/components/schemas/DdCollectionStopped"}},"required":["data"],"description":"The paused subscription. For a Direct Debit subscription, `dd_collection_stopped` lists the invoices left open and not collected automatically."},"SubscriptionStateError":{"type":"object","properties":{"error":{"type":"object","properties":{"type":{"type":"string","enum":["invalid_request_error"]},"code":{"type":"string","enum":["invalid_status"]},"message":{"type":"string"},"param":{"type":"string"},"request_id":{"type":"string"}},"required":["type","code","message"]}},"required":["error"],"description":"Returned when the subscription's current status does not allow the operation. Nothing was changed."},"PauseSubscriptionInput":{"type":"object","properties":{},"description":"A pause takes no parameters. Send no body, or an empty object. Unknown properties are ignored."},"SubscriptionResumeResponse":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Subscription"}},"required":["data"],"description":"The resumed subscription, with its current period and next billing date moved past the paused periods."},"SubscriptionResumeConflictError":{"type":"object","properties":{"error":{"type":"object","properties":{"type":{"type":"string","enum":["invalid_request_error"]},"code":{"type":"string","enum":["mandate_not_usable","direct_debit_not_configured"]},"message":{"type":"string"},"param":{"type":"string"},"request_id":{"type":"string"},"details":{"type":"object","properties":{"mandate_id":{"type":"string","format":"uuid","description":"The Direct Debit mandate the subscription collects against."},"mandate_usability_code":{"type":"string","description":"Why the mandate cannot collect, for example `local_cancelled`, `local_suspended`, `provider_status_pending` or `mandate_missing`."}}}},"required":["type","code","message"]}},"required":["error"],"description":"Returned when a Direct Debit subscription cannot resume because its mandate cannot collect, or Direct Debit is not configured for the merchant. Nothing was changed; the subscription stays paused."},"SubscriptionCancelSubscriptionResponse":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Subscription"},"dd_collection_stopped":{"$ref":"#/components/schemas/DdCollectionStopped"}},"required":["data"],"description":"The subscription after being canceled with the specified cancellation parameters."},"CancelSubscriptionInput":{"type":"object","properties":{"mode":{"type":"string","enum":["immediately","period_end"],"description":"Cancel mode: \"immediately\" revokes access now, \"period_end\" schedules cancellation at billing period end"},"reason":{"type":"string","description":"Optional reason for cancellation"}},"required":["mode"],"description":"Parameters for canceling a subscription, including whether to cancel immediately or at period end."},"SubscriptionReactivateResponse":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Subscription"}},"required":["data"],"description":"The subscription after being reactivated from a canceled state, with updated billing schedule."},"SubscriptionItemListResponse":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/SubscriptionItem"}},"meta":{"type":"object","properties":{"total":{"type":"number","example":10},"limit":{"type":"number","example":100},"offset":{"type":"number","example":0},"has_more":{"type":"boolean","example":false}},"required":["total","limit","offset","has_more"]}},"required":["data","meta"],"description":"A list of line items in a subscription."},"SubscriptionItem":{"type":"object","properties":{"id":{"type":"string","format":"uuid","example":"550e8400-e29b-41d4-a716-446655440000"},"subscription_id":{"type":"string","format":"uuid","example":"660e8400-e29b-41d4-a716-446655440000"},"product_id":{"type":["string","null"],"format":"uuid","example":"770e8400-e29b-41d4-a716-446655440000"},"price_id":{"type":["string","null"],"format":"uuid","example":"880e8400-e29b-41d4-a716-446655440000"},"description":{"type":["string","null"],"example":"Pro Plan - Monthly"},"quantity":{"type":"integer","minimum":1,"example":1,"description":"Item quantity"},"currency":{"type":"string","example":"USD"},"unit_amount_minor":{"type":"integer","example":2999,"description":"Unit price in cents"},"fulfillment_type":{"type":"string","enum":["none","physical","digital"],"example":"none","description":"none: service (no order), physical: creates shipping order, digital: creates download order"},"metadata":{"type":"object","additionalProperties":{},"description":"Custom key-value metadata"},"created_at":{"type":"string","format":"date-time","example":"2024-01-15T10:30:00Z"},"updated_at":{"type":"string","format":"date-time","example":"2024-01-15T10:30:00Z"}},"required":["id","subscription_id","product_id","price_id","description","quantity","currency","unit_amount_minor","fulfillment_type","created_at","updated_at"],"description":"A line item within a subscription, representing a specific product and price that the customer is billed for each cycle."},"SubscriptionItemCreateResponse":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/SubscriptionItem"}},"required":["data"],"description":"The newly added subscription line item."},"CreateSubscriptionItemInput":{"type":"object","properties":{"product_id":{"type":"string","format":"uuid","description":"Product UUID to link to this item"},"price_id":{"type":"string","format":"uuid","description":"Price UUID to link to this item"},"description":{"type":"string","maxLength":500,"description":"Item description"},"quantity":{"type":"integer","minimum":1,"default":1,"description":"Item quantity","example":1},"currency":{"type":"string","minLength":3,"maxLength":3,"description":"ISO 4217 currency code","example":"USD"},"unit_amount_minor":{"type":"integer","minimum":0,"description":"Unit price in cents","example":2999},"fulfillment_type":{"type":"string","enum":["none","physical","digital"],"default":"none","description":"Determines if orders are created on renewal","example":"none"},"metadata":{"type":"object","additionalProperties":{},"description":"Custom key-value metadata"}},"required":["currency","unit_amount_minor"],"description":"Parameters for adding a new line item to an existing subscription."},"SubscriptionItemUpdateResponse":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/SubscriptionItem"}},"required":["data"],"description":"The updated subscription line item reflecting the changes."},"UpdateSubscriptionItemInput":{"type":"object","properties":{"quantity":{"type":"integer","minimum":1,"description":"Item quantity"},"description":{"type":"string","maxLength":500,"description":"Item description"},"unit_amount_minor":{"type":"integer","minimum":0,"description":"Unit price in cents"},"fulfillment_type":{"type":"string","enum":["none","physical","digital"],"description":"Determines if orders are created on renewal"},"metadata":{"type":"object","additionalProperties":{},"description":"Custom key-value metadata"},"price_id":{"type":"string","format":"uuid","description":"Swap the item onto another Price. Allowed only when the new Price shares the same meter_id (fail closed otherwise)."}},"description":"Parameters for updating a subscription line item, such as changing the quantity."},"SubscriptionItemDeleteResponse":{"type":"object","properties":{"success":{"type":"boolean"},"deleted_id":{"type":"string","format":"uuid"}},"required":["success","deleted_id"],"description":"Confirmation that the subscription line item was removed."},"AnalyticsMrrSummaryValidationError":{"type":"object","properties":{"error":{"type":"object","properties":{"type":{"type":"string","enum":["invalid_request_error"]},"code":{"type":"string","enum":["parameter_invalid"]},"message":{"type":"string"},"request_id":{"type":"string"},"param":{"type":"string","enum":["asOfDate"],"description":"The query parameter that was rejected."}},"required":["type","code","message","param"]}},"required":["error"],"description":"Returned when a query parameter fails validation. `param` names the parameter to fix."},"AnalyticsRevenueTimeSeriesValidationError":{"type":"object","properties":{"error":{"type":"object","properties":{"type":{"type":"string","enum":["invalid_request_error"]},"code":{"type":"string","enum":["parameter_invalid"]},"message":{"type":"string"},"request_id":{"type":"string"},"param":{"type":"string","enum":["startDate","endDate","granularity"],"description":"The query parameter that was rejected."}},"required":["type","code","message","param"]}},"required":["error"],"description":"Returned when a query parameter fails validation. `param` names the parameter to fix."},"Payout":{"type":"object","properties":{"id":{"type":"string","format":"uuid","example":"550e8400-e29b-41d4-a716-446655440000"},"public_id":{"type":"string","example":"pyt_abc123xyz"},"gateway":{"type":"string","description":"Name of the payment processor that produced this payout batch"},"gateway_payout_id":{"type":"string","example":"batch_12345"},"gross_amount_minor":{"type":"integer","example":100000,"description":"Gross amount in cents"},"fees_amount_minor":{"type":"integer","example":2500,"description":"Fees in cents"},"net_amount_minor":{"type":"integer","example":97500,"description":"Net amount in cents"},"currency":{"type":"string","example":"USD"},"charges_count":{"type":["number","null"],"example":42},"refunds_count":{"type":["number","null"],"example":2},"chargebacks_count":{"type":["number","null"],"example":0},"status":{"type":"string","enum":["pending","in_transit","paid","failed","canceled"],"example":"paid"},"arrival_date":{"type":["string","null"],"example":"2024-01-15"},"settled_at":{"type":["string","null"],"format":"date-time","example":"2024-01-15T10:30:00Z"},"created_at":{"type":"string","format":"date-time","example":"2024-01-14T08:00:00Z"}},"required":["id","public_id","gateway","gateway_payout_id","gross_amount_minor","fees_amount_minor","net_amount_minor","currency","charges_count","refunds_count","chargebacks_count","status","arrival_date","settled_at","created_at"],"description":"A payout represents funds settled from processed payments to your bank account. Track settlement batches and reconcile amounts."},"PayoutPayment":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"public_id":{"type":["string","null"]},"amount_minor":{"type":"integer"},"currency":{"type":"string"},"status":{"type":"string"},"kind":{"type":["string","null"]},"paid_at":{"type":["string","null"],"format":"date-time"},"created_at":{"type":"string","format":"date-time"}},"required":["id","public_id","amount_minor","currency","status","kind","paid_at","created_at"],"description":"A payment included in a payout settlement batch, with amount and fee details."},"RefundListResponse":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Refund"}},"meta":{"type":"object","properties":{"total":{"type":"number","example":100,"description":"Total number of results matching the query"},"limit":{"type":"number","example":20,"description":"Maximum results per page"},"offset":{"type":"number","example":0,"description":"Number of results skipped"},"has_more":{"type":"boolean","example":true,"description":"Whether more results exist beyond this page"}},"required":["total","limit","offset","has_more"]}},"required":["data","meta"],"description":"A paginated list of refunds matching the query filters.","example":{"data":[{"id":"1b2c3d4e-5f6a-4b87-9c98-0d1e2f3a4b86","public_id":"ref_example_7k2m","payment_id":"7b8c9d0e-1f2a-4b43-9d4e-5f6a7b8c9d42","parent_transaction_id":"7b8c9d0e-1f2a-4b43-9d4e-5f6a7b8c9d42","order_id":null,"gateway":"nmi","gateway_refund_id":"10482739012","amount_minor":1176,"currency":"GBP","reason":"customer_request","reason_details":"Goodwill credit: one week of the Pro plan.","status":"succeeded","failure_reason":null,"failure_code":null,"processed_at":"2026-04-20T14:31:00.000Z","created_at":"2026-04-20T14:30:00.000Z"}],"meta":{"total":1,"limit":20,"offset":0,"has_more":false}}},"Refund":{"type":"object","properties":{"id":{"type":"string","format":"uuid","example":"550e8400-e29b-41d4-a716-446655440000"},"public_id":{"type":"string","example":"ref_abc123xyz"},"payment_id":{"type":"string","format":"uuid","example":"660e8400-e29b-41d4-a716-446655440000","description":"The original payment ID this refund is for (alias for parent_transaction_id)"},"parent_transaction_id":{"type":"string","format":"uuid","example":"660e8400-e29b-41d4-a716-446655440000","description":"Parent transaction ID in unified transaction model. Same as payment_id for refunds."},"order_id":{"type":["string","null"],"format":"uuid","example":null},"gateway":{"type":"string","description":"Name of the payment processor that handled this refund"},"gateway_refund_id":{"type":["string","null"],"example":"txn_refund_12345"},"amount_minor":{"type":"integer","example":5000,"description":"Refund amount in cents"},"currency":{"type":"string","example":"USD"},"reason":{"type":["string","null"],"enum":["customer_request","duplicate","fraudulent","product_not_received","product_unacceptable","subscription_canceled","other",null],"example":"customer_request"},"reason_details":{"type":["string","null"],"example":"Customer changed their mind"},"status":{"type":"string","enum":["pending","processing","succeeded","failed"],"example":"succeeded"},"failure_reason":{"type":["string","null"],"example":null},"failure_code":{"type":["string","null"],"example":null},"processed_at":{"type":["string","null"],"format":"date-time","example":"2024-01-15T10:30:00Z"},"created_at":{"type":"string","format":"date-time","example":"2024-01-14T08:00:00Z"}},"required":["id","public_id","payment_id","parent_transaction_id","order_id","gateway","gateway_refund_id","amount_minor","currency","reason","reason_details","status","failure_reason","failure_code","processed_at","created_at"],"description":"A refund represents money returned to a customer after a completed payment. Refunds create a child transaction linked to the original payment for audit trail purposes.","example":{"id":"1b2c3d4e-5f6a-4b87-9c98-0d1e2f3a4b86","public_id":"ref_example_7k2m","payment_id":"7b8c9d0e-1f2a-4b43-9d4e-5f6a7b8c9d42","parent_transaction_id":"7b8c9d0e-1f2a-4b43-9d4e-5f6a7b8c9d42","order_id":null,"gateway":"nmi","gateway_refund_id":"10482739012","amount_minor":1176,"currency":"GBP","reason":"customer_request","reason_details":"Goodwill credit: one week of the Pro plan.","status":"succeeded","failure_reason":null,"failure_code":null,"processed_at":"2026-04-20T14:31:00.000Z","created_at":"2026-04-20T14:30:00.000Z"}},"RefundRetrieveResponse":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Refund"}},"required":["data"],"description":"A single refund object with full details.","example":{"data":{"id":"1b2c3d4e-5f6a-4b87-9c98-0d1e2f3a4b86","public_id":"ref_example_7k2m","payment_id":"7b8c9d0e-1f2a-4b43-9d4e-5f6a7b8c9d42","parent_transaction_id":"7b8c9d0e-1f2a-4b43-9d4e-5f6a7b8c9d42","order_id":null,"gateway":"nmi","gateway_refund_id":"10482739012","amount_minor":1176,"currency":"GBP","reason":"customer_request","reason_details":"Goodwill credit: one week of the Pro plan.","status":"succeeded","failure_reason":null,"failure_code":null,"processed_at":"2026-04-20T14:31:00.000Z","created_at":"2026-04-20T14:30:00.000Z"}}},"RefundRetrieveValidationError":{"type":"object","properties":{"error":{"type":"object","properties":{"type":{"type":"string","enum":["invalid_request_error"]},"code":{"type":"string","enum":["parameter_invalid"]},"message":{"type":"string"},"request_id":{"type":"string"},"param":{"type":"string","enum":["id"],"description":"The query parameter that was rejected."}},"required":["type","code","message","param"]}},"required":["error"],"description":"Returned when a query parameter fails validation. `param` names the parameter to fix."},"RefundCreateResponse":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Refund"}},"required":["data"],"description":"The newly created refund object, including the gateway response details.","example":{"data":{"id":"1b2c3d4e-5f6a-4b87-9c98-0d1e2f3a4b86","public_id":"ref_example_7k2m","payment_id":"7b8c9d0e-1f2a-4b43-9d4e-5f6a7b8c9d42","parent_transaction_id":"7b8c9d0e-1f2a-4b43-9d4e-5f6a7b8c9d42","order_id":null,"gateway":"nmi","gateway_refund_id":"10482739012","amount_minor":1176,"currency":"GBP","reason":"customer_request","reason_details":"Goodwill credit: one week of the Pro plan.","status":"succeeded","failure_reason":null,"failure_code":null,"processed_at":"2026-04-20T14:31:00.000Z","created_at":"2026-04-20T14:30:00.000Z"}}},"CreateRefundInput":{"type":"object","properties":{"payment_id":{"type":"string","format":"uuid","description":"The sale transaction to refund: a `transactions.id` (UUID), as returned by `GET /v2/transactions` or a charge. Name the sale with exactly one of `payment_id`, `transaction` or `payment_intent`. The sale must be `succeeded`, `settled` or `partially_refunded`; several partial refunds are allowed until the sale is fully refunded.","example":"660e8400-e29b-41d4-a716-446655440000"},"transaction":{"type":"string","format":"uuid","description":"The same as `payment_id`: the `transactions.id` of the sale. Send exactly one of `payment_id`, `transaction` or `payment_intent`.","example":"660e8400-e29b-41d4-a716-446655440000"},"payment_intent":{"type":"string","pattern":"^(?:pi_[A-Za-z0-9]+|[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12})$","description":"A payment intent (`pi_...` id or UUID) that was charged: the refund is for the sale it recorded (its `transaction`). Answers 404 `resource_missing` for an intent that is not yours and 409 `payment_intent_not_charged` for one that has no sale. Send exactly one of `payment_id`, `transaction` or `payment_intent`.","example":"pi_3Nk8vFg2xQ"},"amount_minor":{"type":"integer","minimum":1,"description":"Amount to refund in cents. If not provided, refunds the full remaining amount.","example":5000},"reason":{"type":"string","enum":["customer_request","duplicate","fraudulent","product_not_received","product_unacceptable","subscription_canceled","other"],"description":"Reason for the refund","example":"customer_request"},"reason_details":{"type":"string","maxLength":500,"description":"Additional details about the refund reason","example":"Customer requested refund via support ticket #12345"},"restock_line_items":{"type":"array","items":{"type":"object","properties":{"invoice_line_item_id":{"type":"string","format":"uuid","description":"An invoice line of the refunded payment"},"quantity":{"type":"integer","minimum":1,"description":"Units of that line to put back on sale","example":1}},"required":["invoice_line_item_id","quantity"]},"maxItems":100,"description":"Units to put back on sale once the refund succeeds, by invoice line. Omit to restock nothing: an amount alone never restocks."}},"description":"Parameters for creating a refund. Name the sale with exactly one of `payment_id`, `transaction` or `payment_intent`, and optionally a partial amount."},"VoidListResponse":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Void"}},"meta":{"type":"object","properties":{"total":{"type":"number","example":100,"description":"Total number of results matching the query"},"limit":{"type":"number","example":20,"description":"Maximum results per page"},"offset":{"type":"number","example":0,"description":"Number of results skipped"},"has_more":{"type":"boolean","example":true,"description":"Whether more results exist beyond this page"}},"required":["total","limit","offset","has_more"]}},"required":["data","meta"],"description":"A paginated list of voids matching the query filters."},"Void":{"type":"object","properties":{"id":{"type":"string","format":"uuid","example":"550e8400-e29b-41d4-a716-446655440000"},"public_id":{"type":"string","example":"void_abc123xyz"},"payment_id":{"type":"string","format":"uuid","example":"660e8400-e29b-41d4-a716-446655440000","description":"The original payment ID this void is for (alias for parent_transaction_id)"},"parent_transaction_id":{"type":"string","format":"uuid","example":"660e8400-e29b-41d4-a716-446655440000","description":"Parent transaction ID in unified transaction model."},"order_id":{"type":["string","null"],"format":"uuid","example":null},"gateway":{"type":"string","description":"Name of the payment processor that handled this void"},"gateway_void_id":{"type":["string","null"],"example":"txn_void_12345"},"amount_minor":{"type":"integer","example":10000,"description":"Void amount in cents (always full amount of original transaction)"},"currency":{"type":"string","example":"USD"},"reason":{"type":["string","null"],"example":"Customer requested cancellation"},"reason_code":{"type":["string","null"],"enum":["customer_request","duplicate","fraudulent","error","other",null],"example":"customer_request"},"status":{"type":"string","enum":["pending","processing","succeeded","failed"],"example":"succeeded"},"failure_reason":{"type":["string","null"],"example":null},"failure_code":{"type":["string","null"],"example":null},"voided_at":{"type":["string","null"],"format":"date-time","example":"2024-01-15T10:30:00Z"},"created_at":{"type":"string","format":"date-time","example":"2024-01-14T08:00:00Z"}},"required":["id","public_id","payment_id","parent_transaction_id","order_id","gateway","gateway_void_id","amount_minor","currency","reason","reason_code","status","failure_reason","failure_code","voided_at","created_at"],"description":"A void cancels an unsettled payment before gateway settlement. Voids are always for the full amount — use a refund for settled payments."},"VoidRetrieveResponse":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Void"}},"required":["data"],"description":"A single void object with full details."},"VoidCreateResponse":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Void"}},"required":["data"],"description":"The newly created void object confirming the unsettled payment was canceled."},"CreateVoidInput":{"type":"object","properties":{"payment_id":{"type":"string","format":"uuid","description":"The ID of the payment to void. Must be unsettled.","example":"660e8400-e29b-41d4-a716-446655440000"},"reason":{"type":"string","maxLength":500,"description":"Reason for the void","example":"Customer requested cancellation before shipment"},"reason_code":{"type":"string","enum":["customer_request","duplicate","fraudulent","error","other"],"description":"Standardized reason code for the void","example":"customer_request"}},"required":["payment_id"],"description":"Parameters for creating a void. Specify the transaction ID of the unsettled payment to cancel."},"CreditNoteListResponse":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/CreditNote"}},"requestId":{"type":"string"},"meta":{"type":"object","properties":{"total":{"type":"number","example":100},"limit":{"type":"number","example":20},"offset":{"type":"number","example":0},"has_more":{"type":"boolean","example":true}},"required":["total","limit","offset","has_more"]}},"required":["data","requestId"],"description":"A paginated list of credit notes matching the query filters."},"CreditNoteRetrieveResponse":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/CreditNote"}},"required":["data"],"description":"A single credit note with full details including the associated invoice and refund."},"CreditNoteCreateResponse":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/CreditNote"}},"required":["data"],"description":"The newly created credit note with issued status and PDF URL."},"CreateCreditNoteInput":{"type":"object","properties":{"invoice_id":{"type":"string","format":"uuid","description":"The ID of the invoice to issue a credit note for","example":"660e8400-e29b-41d4-a716-446655440000"},"amount_minor":{"type":"integer","minimum":1,"maximum":2000000000,"description":"Amount to credit in minor units (cents). Capped at 2,000,000,000 to stay below the 32-bit integer ceiling of the underlying DB column.","example":5000},"tax_amount_minor":{"type":"integer","minimum":0,"maximum":2000000000,"description":"Tax portion of the credit in minor units (cents). For UK/EU VAT compliance. Capped at 2,000,000,000.","example":500},"line_allocation_version":{"type":"string","enum":["v1"],"description":"Version of the explicit credit-note line allocation contract","example":"v1"},"line_allocations":{"type":"array","items":{"$ref":"#/components/schemas/CreditNoteLineAllocationInputV1"},"minItems":1,"description":"Exact source invoice lines and amounts being credited"},"credit_method":{"type":"string","enum":["refund_to_payment_method","customer_balance","external"],"default":"refund_to_payment_method","description":"How the credit should be applied","example":"refund_to_payment_method"},"reason":{"type":"string","maxLength":500,"description":"Reason for the credit note","example":"Service not provided as agreed"},"reason_code":{"type":"string","enum":["customer_request","duplicate_charge","service_issue","billing_error","subscription_cancellation","proration","other"],"description":"Standardized reason code","example":"customer_request"},"cancel_subscription":{"type":"boolean","default":false,"description":"Whether to cancel the associated subscription after issuing","example":false},"is_prorated":{"type":"boolean","description":"Whether this credit note represents a prorated amount","example":false},"proration_days_total":{"type":"integer","minimum":1,"maximum":366,"description":"Total days in the billing period (for prorated credits). Constrained to 1–366.","example":30},"proration_days_unused":{"type":"integer","minimum":0,"maximum":366,"description":"Unused days in the billing period (for prorated credits). Must be <= proration_days_total. Constrained to 0–366.","example":12},"idempotency_key":{"type":"string","maxLength":255,"description":"Idempotency key to prevent duplicate credit notes"},"metadata":{"type":"object","additionalProperties":{},"description":"Arbitrary key-value metadata"},"auto_route":{"type":"boolean","default":false,"description":"When true, automatically trigger the process-credit-note task for multi-gateway reversal routing. Response will include gateway_operations array. Always combine with `idempotency_key` to avoid duplicate gateway operations.","example":false}},"required":["invoice_id","amount_minor"],"description":"Parameters for creating a credit note against an invoice, specifying the amount, reason, and whether to issue a refund."},"CreditNoteVoidResponse":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/CreditNote"}},"required":["data"],"description":"The credit note after being voided, reversing its accounting effect."},"CreditNoteEligibilityResponse":{"type":"object","properties":{"invoice_id":{"type":"string","format":"uuid"},"total_amount_minor":{"type":"integer"},"total_paid_minor":{"type":"integer"},"total_credited_minor":{"type":"integer"},"max_creditable_minor":{"type":"integer"},"eligible":{"type":"boolean"},"payments":{"type":"array","items":{"type":"object","properties":{"transaction_id":{"type":"string","format":"uuid"},"gateway":{"type":"string"},"payment_method":{"type":["string","null"]},"amount_minor":{"type":"integer"},"amount_refunded_minor":{"type":"integer"},"amount_available_minor":{"type":"integer"},"card_brand":{"type":["string","null"]},"card_last4":{"type":["string","null"]},"terminal_serial":{"type":["string","null"]},"available_operations":{"type":"array","items":{"type":"string"}},"constraints":{"type":"object","properties":{"partial_supported":{"type":"boolean"},"requires_card_present":{"type":"boolean"},"requires_terminal_online":{"type":"boolean"}},"required":["partial_supported","requires_card_present","requires_terminal_online"]}},"required":["transaction_id","gateway","payment_method","amount_minor","amount_refunded_minor","amount_available_minor","card_brand","card_last4","terminal_serial","available_operations","constraints"]}}},"required":["invoice_id","total_amount_minor","total_paid_minor","total_credited_minor","max_creditable_minor","eligible","payments"],"description":"Eligibility details showing whether a credit note can be issued for the specified invoice and the maximum creditable amount."},"TransactionReversalEligibilityResponse":{"type":"object","properties":{"can_reverse":{"type":"boolean"},"transaction_id":{"type":"string","format":"uuid"},"gateway":{"type":"string"},"payment_method":{"type":["string","null"]},"available_operations":{"type":"array","items":{"type":"string"}},"constraints":{"type":"object","properties":{"max_amount":{"type":"number"},"partial_supported":{"type":"boolean"},"requires_card_present":{"type":"boolean"},"requires_terminal_online":{"type":"boolean"},"requires_customer_iban":{"type":"boolean"},"reason":{"type":["string","null"]}},"required":["max_amount","partial_supported","requires_card_present","requires_terminal_online","requires_customer_iban","reason"]},"card_brand":{"type":["string","null"]},"card_last4":{"type":["string","null"]},"terminal_serial":{"type":["string","null"]},"terminal_uti":{"type":["string","null"]}},"required":["can_reverse","transaction_id","gateway","payment_method","available_operations","constraints","card_brand","card_last4","terminal_serial","terminal_uti"],"description":"Details about which reversal methods (refund, void, or credit note) are available for a specific transaction."},"CreditNoteLineList":{"type":"object","properties":{"object":{"type":"string","enum":["list"]},"data":{"type":"array","items":{"$ref":"#/components/schemas/CreditNoteLine"}},"has_more":{"type":"boolean"},"url":{"type":"string"}},"required":["object","data","has_more","url"]},"CreditNoteLine":{"type":"object","properties":{"id":{"type":"string","format":"uuid","example":"abcd1234-e29b-41d4-a716-446655440000"},"object":{"type":"string","enum":["credit_note_line"]},"credit_note_id":{"type":"string","format":"uuid"},"invoice_line_item_id":{"type":["string","null"],"format":"uuid","description":"The invoice line item this credit line applies to, when the credit was scoped to a specific line. Null for invoice-level credits."},"description":{"type":"string","example":"Pro Plan subscription — prorated for unused 12 days"},"quantity":{"type":"integer","example":1},"unit_amount_minor":{"type":"integer","description":"Per-unit credit amount in cents","example":5000},"total_amount_minor":{"type":"integer","description":"Total credit for this line in cents","example":5000},"metadata":{"type":["object","null"],"additionalProperties":{}},"created_at":{"type":"string","format":"date-time"}},"required":["id","object","credit_note_id","invoice_line_item_id","description","quantity","unit_amount_minor","total_amount_minor","metadata","created_at"],"description":"A single line item on a credit note. Each line represents a portion of the invoice that is being credited — either a whole invoice line or a prorated slice of one."},"CreditNotePreviewResponse":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/CreditNotePreview"}},"required":["data"]},"CreditNotePreview":{"type":"object","properties":{"object":{"type":"string","enum":["credit_note_preview"]},"invoice_id":{"type":"string","format":"uuid"},"proposed_amount_minor":{"type":"integer"},"proposed_tax_amount_minor":{"type":["integer","null"]},"currency":{"type":"string"},"invoice":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"total_minor":{"type":"integer"},"amount_paid_minor":{"type":"integer"},"amount_due_minor":{"type":"integer"},"previously_credited_minor":{"type":"integer"},"max_creditable_minor":{"type":"integer"}},"required":["id","total_minor","amount_paid_minor","amount_due_minor","previously_credited_minor","max_creditable_minor"]},"after_credit":{"type":"object","properties":{"new_amount_due_minor":{"type":"integer"},"new_amount_credited_minor":{"type":"integer"},"would_leave_outstanding":{"type":"boolean","description":"True if applying this credit would leave a non-zero balance on the invoice."}},"required":["new_amount_due_minor","new_amount_credited_minor","would_leave_outstanding"]},"exceeds_max_creditable":{"type":"boolean","description":"True if `amount_minor` exceeds the invoice's remaining creditable amount. A real create call would be rejected."},"credit_method":{"type":"string"}},"required":["object","invoice_id","proposed_amount_minor","proposed_tax_amount_minor","currency","invoice","after_credit","exceeds_max_creditable","credit_method"],"description":"A non-persistent preview of a credit note. Use this to show a customer (or merchant operator) what the credit would do before committing to creating it."},"PreviewCreditNoteInput":{"type":"object","properties":{"invoice_id":{"type":"string","format":"uuid","description":"Invoice to preview a credit note against.","example":"660e8400-e29b-41d4-a716-446655440000"},"amount_minor":{"type":"integer","minimum":1,"description":"Proposed credit amount in cents. The preview validates that this does not exceed the invoice's outstanding creditable amount.","example":5000},"tax_amount_minor":{"type":"integer","minimum":0,"description":"Proposed tax portion of the credit in cents.","example":500},"credit_method":{"type":"string","enum":["refund_to_payment_method","customer_balance","external"],"description":"Planned credit method. Defaults to refund_to_payment_method.","example":"refund_to_payment_method"},"reason_code":{"type":"string","enum":["customer_request","duplicate_charge","service_issue","billing_error","subscription_cancellation","proration","other"]}},"required":["invoice_id","amount_minor"],"description":"Parameters to preview a credit note without persisting it. The response shows what the credit note would look like if created with these fields."},"Dispute":{"type":"object","properties":{"id":{"type":"string","format":"uuid","example":"550e8400-e29b-41d4-a716-446655440000","description":"Unique identifier for the dispute"},"public_id":{"type":"string","example":"dsp_abc123xyz","description":"Public-facing dispute identifier"},"payment_id":{"type":["string","null"],"format":"uuid","example":"660e8400-e29b-41d4-a716-446655440000","description":"The original payment ID this dispute is for (alias for parent_transaction_id)"},"parent_transaction_id":{"type":["string","null"],"format":"uuid","example":"660e8400-e29b-41d4-a716-446655440000","description":"Parent transaction ID in unified transaction model. Same as payment_id for disputes."},"gateway":{"type":"string","description":"Name of the payment processor that handled the original transaction"},"payment_rail":{"type":["string","null"],"example":"direct_debit","description":"Rail/payment method for the disputed transaction"},"dispute_kind":{"type":["string","null"],"example":"dd_indemnity_claim","description":"Rail-specific dispute subtype, when available"},"gateway_dispute_id":{"type":"string","example":"chargeback_12345","description":"Dispute identifier assigned by the payment gateway"},"gateway_transaction_id":{"type":["string","null"],"example":"txn_original_12345","description":"Original transaction identifier from the payment gateway"},"amount_minor":{"type":"integer","example":10000,"description":"Disputed amount in cents"},"currency":{"type":"string","example":"USD","description":"Three-letter ISO 4217 currency code"},"reason_code":{"type":["string","null"],"example":"4837","description":"Card network reason code"},"reason":{"type":["string","null"],"example":"Fraudulent transaction","description":"Human-readable reason for the dispute"},"network_reason_code":{"type":["string","null"],"example":"83","description":"Card network-specific reason code"},"network_reason_description":{"type":["string","null"],"example":"Fraud - Card-Absent Environment","description":"Card network-specific reason description"},"customer_name":{"type":["string","null"],"example":"John Doe","description":"Name of the customer who filed the dispute"},"card_brand":{"type":["string","null"],"example":"visa","description":"Card brand used for the original payment"},"card_last4":{"type":["string","null"],"example":"4242","description":"Last four digits of the card used for the original payment"},"evidence_due_by":{"type":["string","null"],"example":"2024-01-25T23:59:59Z","description":"Deadline for submitting evidence to contest the dispute"},"evidence_submitted":{"type":"boolean","example":false,"description":"Whether evidence has been submitted for this dispute"},"evidence_submitted_at":{"type":["string","null"],"format":"date-time","example":null,"description":"Timestamp when evidence was submitted"},"status":{"type":"string","enum":["needs_response","under_review","won","lost","warning_closed","warning_needs_response"],"example":"needs_response","description":"Current status of the dispute lifecycle"},"resolution":{"type":["string","null"],"enum":["won","lost","withdrawn",null],"example":null,"description":"Final resolution outcome of the dispute"},"resolved_at":{"type":["string","null"],"format":"date-time","example":null,"description":"Timestamp when the dispute was resolved"},"disputed_at":{"type":"string","format":"date-time","example":"2024-01-15T10:30:00Z","description":"Timestamp when the dispute was filed"},"created_at":{"type":"string","format":"date-time","example":"2024-01-15T10:30:00Z","description":"Timestamp when the dispute record was created"}},"required":["id","public_id","payment_id","parent_transaction_id","gateway","payment_rail","dispute_kind","gateway_dispute_id","gateway_transaction_id","amount_minor","currency","reason_code","reason","network_reason_code","network_reason_description","customer_name","card_brand","card_last4","evidence_due_by","evidence_submitted","evidence_submitted_at","status","resolution","resolved_at","disputed_at","created_at"],"description":"A dispute (chargeback) represents a customer's challenge to a charge with their card issuer. Track lifecycle stages and submit evidence to contest."},"OpenDispute":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Unique identifier for the dispute"},"public_id":{"type":"string","description":"Public-facing dispute identifier"},"payment_id":{"type":["string","null"],"format":"uuid","description":"The original payment ID this dispute is for"},"parent_transaction_id":{"type":["string","null"],"format":"uuid","description":"Parent transaction ID in unified transaction model"},"gateway":{"type":"string","description":"Payment gateway that processed the original transaction"},"payment_rail":{"type":["string","null"],"description":"Rail/payment method for the disputed transaction"},"dispute_kind":{"type":["string","null"],"description":"Rail-specific dispute subtype, when available"},"amount_minor":{"type":"integer","description":"Disputed amount in cents"},"currency":{"type":"string","description":"Three-letter ISO 4217 currency code"},"reason":{"type":["string","null"],"description":"Human-readable reason for the dispute"},"customer_name":{"type":["string","null"],"description":"Name of the customer who filed the dispute"},"card_brand":{"type":["string","null"],"description":"Card brand used for the original payment"},"card_last4":{"type":["string","null"],"description":"Last four digits of the card used for the original payment"},"evidence_due_by":{"type":["string","null"],"description":"Deadline for submitting evidence to contest the dispute"},"days_until_due":{"type":["number","null"],"description":"Number of days remaining until the evidence deadline"},"is_overdue":{"type":"boolean","description":"Whether the evidence submission deadline has passed"},"disputed_at":{"type":"string","format":"date-time","description":"Timestamp when the dispute was filed"}},"required":["id","public_id","payment_id","parent_transaction_id","gateway","payment_rail","dispute_kind","amount_minor","currency","reason","customer_name","card_brand","card_last4","evidence_due_by","days_until_due","is_overdue","disputed_at"],"description":"An open dispute requiring action, with urgency indicators for evidence submission deadlines."},"DiscountListResponse":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Discount"}},"meta":{"type":"object","properties":{"total":{"type":"number","example":100},"limit":{"type":"number","example":20},"offset":{"type":"number","example":0},"has_more":{"type":"boolean","example":true}},"required":["total","limit","offset","has_more"]}},"required":["data","meta"],"description":"A paginated list of discounts matching the query filters."},"Discount":{"type":"object","properties":{"id":{"type":"string","format":"uuid","example":"550e8400-e29b-41d4-a716-446655440000"},"code":{"type":"string","example":"SUMMER20"},"name":{"type":["string","null"],"example":"Summer Sale 20% Off"},"description":{"type":["string","null"],"example":"20% off for summer promotion"},"discount_type":{"type":"string","example":"percentage"},"discount_value":{"type":"number","example":20,"description":"Amount off (cents) or percentage"},"applies_to":{"type":["string","null"],"example":"all"},"product_ids":{"type":["array","null"],"items":{"type":"string"},"example":null},"max_redemptions":{"type":["number","null"],"example":100},"current_redemptions":{"type":["number","null"],"example":25},"valid_from":{"type":["string","null"],"example":"2024-06-01T00:00:00Z"},"valid_until":{"type":["string","null"],"example":"2024-08-31T23:59:59Z"},"recurring_type":{"type":["string","null"],"example":"once"},"recurring_cycles":{"type":["number","null"],"example":null},"first_time_customer":{"type":["boolean","null"],"example":false},"is_active":{"type":["boolean","null"],"example":true},"created_at":{"type":["string","null"],"format":"date-time","example":"2024-01-14T08:00:00Z"},"updated_at":{"type":["string","null"],"format":"date-time","example":"2024-01-14T08:00:00Z"}},"required":["id","code","name","description","discount_type","discount_value","applies_to","product_ids","max_redemptions","current_redemptions","valid_from","valid_until","recurring_type","recurring_cycles","first_time_customer","is_active","created_at","updated_at"],"description":"A discount code that applies a percentage or fixed amount reduction to charges, invoices, or subscriptions."},"DiscountRetrieveResponse":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Discount"}},"required":["data"],"description":"A single discount object with full details and usage statistics."},"DiscountCreateResponse":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Discount"}},"required":["data"],"description":"The newly created discount object."},"CreateDiscountInput":{"type":"object","properties":{"code":{"type":"string","minLength":1,"maxLength":50,"description":"Unique discount code (will be uppercased)","example":"SUMMER20"},"name":{"type":"string","minLength":1,"maxLength":255,"description":"Human-readable name","example":"Summer Sale 20% Off"},"description":{"type":"string","maxLength":1000,"description":"Optional description","example":"20% off for summer promotion"},"discount_type":{"type":"string","enum":["percentage","fixed_amount"],"description":"Type of discount","example":"percentage"},"discount_value":{"type":"number","minimum":0.01,"description":"Discount value - percentage (1-100) or amount in cents","example":20},"applies_to":{"type":"string","enum":["all","specific_products"],"default":"all","description":"Which products the discount applies to","example":"all"},"scope":{"type":"string","enum":["entire_order","specific_products","subscription_only","one_time_only"],"description":"Scope of where discount can be applied","example":"entire_order"},"product_ids":{"type":"array","items":{"type":"string"},"description":"Product IDs if applies_to is 'specific_products'"},"max_redemptions":{"type":"integer","minimum":1,"description":"Maximum total redemptions allowed","example":100},"max_redemptions_per_user":{"type":"integer","minimum":0,"default":0,"description":"Maximum redemptions per customer (0 = unlimited)","example":1},"valid_from":{"type":"string","description":"Start date (ISO 8601)","example":"2024-06-01T00:00:00Z"},"valid_until":{"type":"string","description":"End date (ISO 8601)","example":"2024-08-31T23:59:59Z"},"recurring_type":{"type":"string","enum":["once","forever","repeating"],"description":"How the discount applies to recurring payments","example":"once"},"recurring_cycles":{"type":"integer","minimum":1,"description":"Number of billing cycles (only if recurring_type is 'repeating')","example":3},"first_time_customer":{"type":"boolean","default":false,"description":"Only available to first-time customers","example":false}},"required":["code","name","discount_type","discount_value"],"description":"Parameters for creating a new discount code with amount, type, and usage limits."},"DiscountUpdateResponse":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Discount"}},"required":["data"],"description":"The updated discount object reflecting the changes."},"UpdateDiscountInput":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":255},"description":{"type":["string","null"],"maxLength":1000},"discount_value":{"type":"number","minimum":0.01},"applies_to":{"type":"string","enum":["all","specific_products"]},"scope":{"type":"string","enum":["entire_order","specific_products","subscription_only","one_time_only"]},"product_ids":{"type":["array","null"],"items":{"type":"string"}},"max_redemptions":{"type":["integer","null"],"minimum":1},"max_redemptions_per_user":{"type":"integer","minimum":0},"valid_from":{"type":["string","null"]},"valid_until":{"type":["string","null"]},"is_active":{"type":"boolean"},"is_archived":{"type":"boolean"},"recurring_type":{"type":["string","null"],"enum":["once","forever","repeating",null]},"recurring_cycles":{"type":["integer","null"],"minimum":1},"first_time_customer":{"type":"boolean"}},"description":"Parameters for updating an existing discount. Active discounts can only have metadata and usage limits modified."},"DiscountDeleteResponse":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Discount"}},"required":["data"],"description":"Confirmation that the discount was successfully deleted."},"PaymentLinkRetrieveValidationError":{"type":"object","properties":{"error":{"type":"object","properties":{"type":{"type":"string","enum":["invalid_request_error"]},"code":{"type":"string","enum":["parameter_invalid"]},"message":{"type":"string"},"request_id":{"type":"string"},"param":{"type":"string","enum":["id"],"description":"The query parameter that was rejected."}},"required":["type","code","message","param"]}},"required":["error"],"description":"Returned when a query parameter fails validation. `param` names the parameter to fix."},"PaymentAttemptListResponse":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/PaymentAttempt"}},"meta":{"type":"object","properties":{"count":{"type":"integer","description":"Number of results returned"},"limit":{"type":"integer","description":"Limit used in query"}},"required":["count"]}},"required":["data","meta"],"description":"A paginated list of payment attempts matching the query filters."},"PaymentAttempt":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Unique identifier for the payment attempt"},"public_id":{"type":["string","null"],"description":"External reference ID (payatt_xxx format). Used as order_id in gateway requests.","example":"payatt_abc123xyz789"},"gateway_order_id":{"type":["string","null"],"description":"Order ID sent to the processor. Format: payatt_<public_id>. Primary correlation key for webhook mapping.","example":"payatt_abc123xyz789"},"gateway_transaction_id":{"type":["string","null"],"description":"Processor-assigned transaction reference.","example":"12345678901"},"payment_id":{"type":["string","null"],"format":"uuid","description":"Logical payment (PaymentIntent) this attempt belongs to"},"invoice_id":{"type":["string","null"],"format":"uuid","description":"Associated invoice ID"},"subscription_id":{"type":["string","null"],"format":"uuid","description":"Associated subscription ID"},"checkout_session_id":{"type":["string","null"],"format":"uuid","description":"Associated checkout session ID"},"billing_run_id":{"type":["string","null"],"format":"uuid","description":"Billing run ID for batch reconciliation"},"gateway":{"type":"string","description":"Name of the payment processor that handled this attempt"},"gateway_code":{"type":"string","description":"Processor-returned response code"},"gateway_message":{"type":"string","description":"Processor-returned response message"},"category":{"type":"string","enum":["approved","generic_decline","insufficient_funds","over_limit","expired_card","invalid_card_number","invalid_cvv","invalid_expiry","invalid_pin","transaction_not_allowed","call_issuer","incorrect_payment_info","lost_or_stolen","pickup_card","suspected_fraud","restricted_card","stop_all_recurring","stop_this_program","update_cardholder_data","processor_error","gateway_error","communication_error","merchant_config_error","merchant_inactive","issuer_unavailable","duplicate_transaction","invalid_transaction","unsupported_card","feature_not_available","authentication_required"],"description":"Normalized error category for the payment attempt"},"severity":{"type":"string","enum":["info","warn","error"],"description":"Severity level for logging/monitoring"},"retryability":{"type":"string","enum":["retry_now","retry_later","do_not_retry"],"description":"Retry behavior recommendation. retry_now: Safe to retry immediately. retry_later: Retry after delay. do_not_retry: Hard decline, don't retry."},"customer_action":{"type":"string","enum":["try_again","use_other_method","update_card","contact_bank","contact_merchant","none"],"description":"Recommended action for the customer"},"merchant_action":{"type":"string","enum":["none","collect_new_pm","fix_config","contact_support","fraud_review","schedule_retry","contact_bank","stop_recurring"],"description":"Recommended action for the merchant"},"subscription_directive":{"type":"string","enum":["none","stop_all","stop_program","update_cardholder_data"],"description":"Directive for subscription handling after decline"},"safe_customer_message_key":{"type":"string","description":"Template key for customer-facing message"},"decline_type":{"type":["string","null"],"enum":["soft","hard",null],"description":"Decline classification. soft: May succeed on retry. hard: Will not succeed on retry."},"attempt_status":{"type":"string","enum":["pending","succeeded","failed","canceled"],"description":"Final outcome status of the attempt"},"amount_cents":{"type":["integer","null"],"description":"Payment amount in cents"},"currency":{"type":["string","null"],"description":"Three-letter ISO currency code"},"payment_method_last4":{"type":["string","null"],"description":"Last 4 digits of card"},"payment_method_brand":{"type":["string","null"],"description":"Card brand (visa, mastercard, etc.)"},"attempt_number":{"type":"integer","description":"Attempt sequence number (1 = first attempt)"},"retry_attempt_number":{"type":"integer","description":"Legacy retry attempt number"},"max_retry_attempts":{"type":["integer","null"],"description":"Maximum retry attempts allowed"},"next_retry_at":{"type":["string","null"],"format":"date-time","description":"Next scheduled retry timestamp"},"is_scheduled_retry":{"type":"boolean","description":"Whether this was a scheduled retry"},"is_manual_retry":{"type":"boolean","description":"Whether this was a manual retry"},"created_at":{"type":"string","format":"date-time","description":"When the attempt was created"},"updated_at":{"type":"string","format":"date-time","description":"When the attempt was last updated"}},"required":["id","public_id","gateway_order_id","gateway_transaction_id","payment_id","invoice_id","subscription_id","checkout_session_id","billing_run_id","gateway","gateway_code","gateway_message","category","severity","retryability","customer_action","merchant_action","subscription_directive","safe_customer_message_key","decline_type","attempt_status","amount_cents","currency","payment_method_last4","payment_method_brand","attempt_number","retry_attempt_number","max_retry_attempts","next_retry_at","is_scheduled_retry","is_manual_retry","created_at","updated_at"],"description":"A record of an individual payment processing attempt, including gateway response codes, AVS/CVV results, and retry metadata."},"PaymentAttemptSingleResponse":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/PaymentAttempt"}},"required":["data"],"description":"A single payment attempt with full gateway response details."},"PaymentAttemptErrorResponse":{"type":"object","properties":{"error":{"type":"string","description":"Error message"}},"required":["error"],"description":"Error response returned when a payment attempt lookup fails."},"RetryEligibilityResponse":{"type":"object","properties":{"data":{"type":"object","properties":{"should_retry":{"type":"boolean","description":"Whether the subscription should be retried"},"reason":{"type":"string","description":"Explanation for the retry decision"},"last_attempt":{"allOf":[{"$ref":"#/components/schemas/PaymentAttempt"},{"type":["object","null"],"description":"Most recent payment attempt"}]}},"required":["should_retry","reason","last_attempt"]}},"required":["data"],"description":"Assessment of whether a failed payment attempt is eligible for retry, including recommended retry strategy."},"DeclineAnalyticsResponse":{"type":"object","properties":{"data":{"type":"object","properties":{"total_attempts":{"type":"integer"},"approved_count":{"type":"integer"},"declined_count":{"type":"integer"},"approval_rate":{"type":"number"},"soft_decline_count":{"type":"integer"},"hard_decline_count":{"type":"integer"},"top_decline_categories":{"type":"array","items":{"type":"object","properties":{"category":{"type":"string"},"count":{"type":"integer"}},"required":["category","count"]}},"by_gateway":{"type":"object","additionalProperties":{"type":"object","properties":{"total":{"type":"integer"},"approved":{"type":"integer"},"declined":{"type":"integer"}}},"description":"Breakdown by gateway"}},"required":["total_attempts","approved_count","declined_count","approval_rate","soft_decline_count","hard_decline_count","top_decline_categories","by_gateway"]}},"required":["data"],"description":"Analytics data about payment decline patterns, including decline codes, rates, and trends."},"PaymentSecurityResponse":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/PaymentSecurityResult"}},"required":["data"],"description":"Response containing the security verification results (AVS/CVV) for a specific payment attempt."},"PaymentSecurityResult":{"type":"object","properties":{"avs":{"$ref":"#/components/schemas/AvsCodeInterpretation"},"cvv":{"$ref":"#/components/schemas/CvvCodeInterpretation"},"overall_risk_level":{"type":"string","enum":["low","medium","high","unknown"],"description":"Combined risk level from AVS + CVV checks"},"recommendation":{"type":"string","enum":["accept","review","decline"],"description":"Recommended action based on security checks"}},"required":["avs","cvv","overall_risk_level","recommendation"],"description":"Combined AVS and CVV verification results for a payment attempt."},"AvsCodeInterpretation":{"type":["object","null"],"properties":{"code":{"type":"string","description":"Raw AVS response code from gateway"},"match_type":{"type":"string","enum":["full_match","partial_match","no_match","not_supported","not_available","error"],"description":"Type of address match"},"address_match":{"type":["boolean","null"],"description":"Whether the street address matched"},"zip_match":{"type":["boolean","null"],"description":"Whether the ZIP/postal code matched"},"description":{"type":"string","description":"Human-readable description of the AVS result"},"risk_level":{"type":"string","enum":["low","medium","high","unknown"],"description":"Risk level based on AVS result. low: full match, medium: partial match, high: no match"}},"required":["code","match_type","address_match","zip_match","description","risk_level"],"description":"AVS verification result"},"CvvCodeInterpretation":{"type":["object","null"],"properties":{"code":{"type":"string","description":"Raw CVV response code from gateway"},"match_type":{"type":"string","enum":["match","no_match","not_processed","not_provided","issuer_not_certified","error"],"description":"Type of CVV match"},"description":{"type":"string","description":"Human-readable description of the CVV result"},"risk_level":{"type":"string","enum":["low","medium","high","unknown"],"description":"Risk level based on CVV result. low: match, medium: not processed, high: no match"}},"required":["code","match_type","description","risk_level"],"description":"CVV verification result"},"ChargeErrorResponse":{"type":"object","properties":{"error":{"type":"string"},"code":{"type":"string"}},"required":["error"],"description":"Error response returned when a charge operation fails due to a gateway decline or validation error."},"ChargeDuplicateErrorResponse":{"type":"object","properties":{"error":{"type":"string"},"existingChargeId":{"type":"string"}},"required":["error"],"description":"Error response returned when a duplicate charge is detected via idempotency key."},"ChargeListResponse":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","minLength":1},"customerId":{"type":"string","minLength":1},"invoiceId":{"type":["string","null"],"minLength":1},"paymentMethodId":{"type":["string","null"],"minLength":1},"amountMinor":{"type":"integer"},"amountCapturedMinor":{"type":"integer"},"amountRefundedMinor":{"type":"integer"},"currency":{"type":"string","minLength":3,"maxLength":3},"status":{"type":"string","enum":["pending","processing","succeeded","failed","refunded","partially_refunded","voided"]},"description":{"type":"string"},"statementDescriptor":{"type":["string","null"]},"failureCode":{"type":["string","null"]},"failureMessage":{"type":["string","null"]},"gatewayTransactionId":{"type":["string","null"]},"receiptUrl":{"type":["string","null"]},"captured":{"type":"boolean"},"metadata":{"type":["object","null"],"additionalProperties":{}},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}},"required":["id","customerId","invoiceId","paymentMethodId","amountMinor","amountCapturedMinor","amountRefundedMinor","currency","status","description","statementDescriptor","failureCode","failureMessage","gatewayTransactionId","receiptUrl","captured","metadata","createdAt","updatedAt"]}},"pagination":{"type":"object","properties":{"page":{"type":"number","description":"Current page number"},"limit":{"type":"number","description":"Maximum results per page"},"total":{"type":"number","description":"Total number of results matching the query"},"totalPages":{"type":"number","description":"Total number of pages available"}},"required":["page","limit","total","totalPages"]}},"required":["data","pagination"],"description":"A paginated list of charges matching the query filters."},"ChargeRetrieveResponse":{"type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string","minLength":1},"customerId":{"type":"string","minLength":1},"invoiceId":{"type":["string","null"],"minLength":1},"paymentMethodId":{"type":["string","null"],"minLength":1},"amountMinor":{"type":"integer"},"amountCapturedMinor":{"type":"integer"},"amountRefundedMinor":{"type":"integer"},"currency":{"type":"string","minLength":3,"maxLength":3},"status":{"type":"string","enum":["pending","processing","succeeded","failed","refunded","partially_refunded","voided"]},"description":{"type":"string"},"statementDescriptor":{"type":["string","null"]},"failureCode":{"type":["string","null"]},"failureMessage":{"type":["string","null"]},"gatewayTransactionId":{"type":["string","null"]},"receiptUrl":{"type":["string","null"]},"captured":{"type":"boolean"},"metadata":{"type":["object","null"],"additionalProperties":{}},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}},"required":["id","customerId","invoiceId","paymentMethodId","amountMinor","amountCapturedMinor","amountRefundedMinor","currency","status","description","statementDescriptor","failureCode","failureMessage","gatewayTransactionId","receiptUrl","captured","metadata","createdAt","updatedAt"]}},"required":["data"],"description":"A single charge object with full details."},"ChargeCaptureResponse":{"type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string","minLength":1},"customerId":{"type":"string","minLength":1},"invoiceId":{"type":["string","null"],"minLength":1},"paymentMethodId":{"type":["string","null"],"minLength":1},"amountMinor":{"type":"integer"},"amountCapturedMinor":{"type":"integer"},"amountRefundedMinor":{"type":"integer"},"currency":{"type":"string","minLength":3,"maxLength":3},"status":{"type":"string","enum":["pending","processing","succeeded","failed","refunded","partially_refunded","voided"]},"description":{"type":"string"},"statementDescriptor":{"type":["string","null"]},"failureCode":{"type":["string","null"]},"failureMessage":{"type":["string","null"]},"gatewayTransactionId":{"type":["string","null"]},"receiptUrl":{"type":["string","null"]},"captured":{"type":"boolean"},"metadata":{"type":["object","null"],"additionalProperties":{}},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}},"required":["id","customerId","invoiceId","paymentMethodId","amountMinor","amountCapturedMinor","amountRefundedMinor","currency","status","description","statementDescriptor","failureCode","failureMessage","gatewayTransactionId","receiptUrl","captured","metadata","createdAt","updatedAt"]}},"required":["data"],"description":"The charge object after capturing the authorized funds."},"ChargeRefundResponse":{"type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string","minLength":1},"customerId":{"type":"string","minLength":1},"invoiceId":{"type":["string","null"],"minLength":1},"paymentMethodId":{"type":["string","null"],"minLength":1},"amountMinor":{"type":"integer"},"amountCapturedMinor":{"type":"integer"},"amountRefundedMinor":{"type":"integer"},"currency":{"type":"string","minLength":3,"maxLength":3},"status":{"type":"string","enum":["pending","processing","succeeded","failed","refunded","partially_refunded","voided"]},"description":{"type":"string"},"statementDescriptor":{"type":["string","null"]},"failureCode":{"type":["string","null"]},"failureMessage":{"type":["string","null"]},"gatewayTransactionId":{"type":["string","null"]},"receiptUrl":{"type":["string","null"]},"captured":{"type":"boolean"},"metadata":{"type":["object","null"],"additionalProperties":{}},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}},"required":["id","customerId","invoiceId","paymentMethodId","amountMinor","amountCapturedMinor","amountRefundedMinor","currency","status","description","statementDescriptor","failureCode","failureMessage","gatewayTransactionId","receiptUrl","captured","metadata","createdAt","updatedAt"]}},"required":["data"],"description":"The charge object after processing a full or partial refund."},"BillingOverviewResponse":{"type":"object","properties":{"overdue_invoices":{"type":"object","properties":{"count":{"type":"integer"},"totals":{"type":"array","items":{"type":"object","properties":{"currency":{"type":"string"},"count":{"type":"integer"},"amount_due_minor":{"type":"integer"}},"required":["currency","count","amount_due_minor"]}},"items":{"type":"array","items":{"type":"object","additionalProperties":{}}}},"required":["count","totals","items"]},"dunning":{"type":"object","properties":{"count":{"type":"integer"},"items":{"type":"array","items":{"type":"object","additionalProperties":{}}}},"required":["count","items"]},"usage":{"type":"object","properties":{"count":{"type":"integer"},"items":{"type":"array","items":{"type":"object","additionalProperties":{}}}},"required":["count","items"]}},"required":["overdue_invoices","dunning","usage"],"description":"Merchant billing snapshot: overdue invoices, dunning subscriptions, and usage invoices.","example":{"overdue_invoices":{"count":1,"totals":[{"currency":"GBP","count":1,"amount_due_minor":5880}],"items":[{"id":"5e6f7a8b-9c0d-46b8-afc6-3b4c5d6e7f89","customer_id":"4d5e6f7a-8b9c-46a7-9eb5-2a3b4c5d6e78","status":"overdue","amount_due_minor":5880,"currency":"GBP","due_date":"2026-04-30T00:00:00.000Z"}]},"dunning":{"count":1,"items":[{"id":"6f7a8b9c-0d1e-46c9-80d7-4c5d6e7f8a90","customer_id":"4d5e6f7a-8b9c-46a7-9eb5-2a3b4c5d6e78","status":"past_due","amount_minor":4900,"currency":"GBP"}]},"usage":{"count":0,"items":[]}}},"BillingPreviewResponse":{"type":"object","properties":{"data":{"type":"object","properties":{"items":{"type":"array","items":{"type":"object","properties":{"sequence":{"type":"integer","minimum":1},"date":{"type":"string","format":"date-time"},"formattedDate":{"type":"string"},"dayName":{"type":"string"},"amountMinor":{"type":"integer"},"formattedAmount":{"type":"string"},"isTrialEnd":{"type":"boolean"},"isFirstCharge":{"type":"boolean"},"isFinalPayment":{"type":"boolean"}},"required":["sequence","date","formattedDate","dayName","amountMinor","formattedAmount"]}},"summary":{"type":"string"},"totalAmountMinor":{"type":"integer"},"firstChargeDate":{"type":"string","format":"date-time"},"trialEndDate":{"type":["string","null"],"format":"date-time"},"scheduleEndDate":{"type":["string","null"],"format":"date-time"},"timezone":{"type":"string"}},"required":["items","summary","totalAmountMinor","firstChargeDate","trialEndDate","scheduleEndDate","timezone"]}},"required":["data"],"description":"A preview of upcoming billing charges for a subscription, showing line items, amounts, and billing dates."},"BillingPreviewErrorResponse":{"type":"object","properties":{"error":{"type":"string"},"field":{"type":"string"}},"required":["error"],"description":"Error response when a billing preview cannot be generated."},"BillingIntervalsResponse":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"value":{"type":"string"},"label":{"type":"string"},"description":{"type":"string"}},"required":["value","label","description"]}}},"required":["data"],"description":"Available billing intervals (monthly, quarterly, yearly) with their configuration and pricing multipliers."},"BillingAnchorRulesResponse":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"value":{"type":"string"},"label":{"type":"string"},"description":{"type":"string"},"requiresAnchorDay":{"type":"boolean"}},"required":["value","label","description","requiresAnchorDay"]}}},"required":["data"],"description":"Billing anchor rules that determine how billing dates are calculated for different start modes and intervals."},"PriceListResponse":{"type":"object","properties":{"object":{"type":"string","enum":["list"]},"data":{"type":"array","items":{"$ref":"#/components/schemas/Price"}},"has_more":{"type":"boolean","example":true},"total_count":{"type":"integer","example":42}},"required":["object","data","has_more"],"description":"A paginated list of prices matching the query filters."},"Price":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Unique identifier for the price","example":"b5d8e2f1-3a6c-4d9b-8e7f-1c4a2b5d6e73"},"object":{"type":"string","enum":["price"],"description":"Object type"},"product_id":{"type":"string","format":"uuid","description":"ID of the product this price belongs to","example":"a7b3c9d2-1e4f-4b6a-8c5d-2f9e0a1b3c42"},"active":{"type":"boolean","description":"Whether the price is active"},"currency":{"type":"string","description":"Three-letter ISO currency code (lowercase)","example":"gbp"},"unit_amount":{"type":["integer","null"],"description":"Price in minor units (cents). Null when the per-unit rate is sub-penny — see `unit_amount_decimal`.","example":4900},"unit_amount_decimal":{"type":["string","null"],"description":"Exact per-unit rate in minor units as a decimal string (sub-penny safe, e.g. \"0.02\" = 0.02 pence). Set when the rate is not a whole minor unit; mirrors Stripe's `unit_amount_decimal`."},"type":{"type":"string","enum":["one_time","recurring"],"description":"Price type","example":"recurring"},"pricing_model":{"type":"string","enum":["fixed","pay_what_you_want","free"],"description":"Pricing model"},"interval":{"type":["string","null"],"enum":["day","week","month","year",null],"description":"Billing interval (recurring only)"},"interval_count":{"type":["integer","null"],"description":"Number of intervals between billings"},"trial_period_days":{"type":["integer","null"],"description":"Trial period in days (recurring only)"},"start_rule":{"type":["string","null"],"enum":["immediate","next_anchor","prorate",null],"description":"First-charge behavior for recurring checkout. `immediate`: full first period now. `next_anchor`: first charge on the billing anchor. `prorate`: prorated stub now, then full charge on the billing anchor."},"minimum_amount":{"type":["integer","null"],"description":"PWYW: minimum amount in cents"},"maximum_amount":{"type":["integer","null"],"description":"PWYW: maximum amount in cents"},"suggested_amount":{"type":["integer","null"],"description":"PWYW: suggested amount in cents"},"preset_amounts":{"type":["array","null"],"items":{"type":"integer"},"description":"PWYW: quick-select amounts"},"name":{"type":["string","null"],"description":"Customer-facing price name (e.g., 'Morning', 'Full day')"},"nickname":{"type":["string","null"],"description":"Display name (e.g., 'Monthly', 'Annual - Save 17%')"},"description":{"type":["string","null"],"description":"Description of this price option"},"version":{"type":"integer","minimum":1,"description":"Immutable version for this price row"},"lookup_key":{"type":["string","null"],"description":"Stable key for API lookups"},"billing_scheme":{"type":"string","enum":["per_unit","tiered","percentage"],"description":"Billing scheme. Defaults to `per_unit`. Set to `tiered` along with `tiers_mode` + `tiers` to use graduated or volume pricing."},"tiers_mode":{"type":["string","null"],"enum":["graduated","volume",null],"description":"Tiered pricing mode. Required when `billing_scheme` is `tiered`, must be null otherwise."},"tiers":{"type":["array","null"],"items":{"$ref":"#/components/schemas/PriceTier"},"description":"Price tiers (ordered by `up_to`). Present only when `billing_scheme` is `tiered`. At least 2 tiers, final tier MUST have `up_to: null`. Immutable after creation."},"transform_quantity":{"type":["object","null"],"properties":{"divide_by":{"type":"integer","exclusiveMinimum":0,"description":"Package size — quantity is divided by this value before pricing. Must be >= 1. Example: `divide_by: 10` with `round: up` charges per pack of 10."},"round":{"type":"string","enum":["up","down"],"description":"Rounding mode when quantity is not an exact multiple of `divide_by`."}},"required":["divide_by","round"],"description":"Optional quantity transform (package pricing) applied before tier lookup. Immutable after creation."},"percentage_rate_bps":{"type":["integer","null"],"description":"Rate in basis points (70 = 0.7%). Present only when `billing_scheme` is `percentage`. Null for `per_unit` and `tiered` prices."},"pay_in_advance":{"type":"boolean","description":"When true for a metered UBB price, usage events are debited from the customer's prepaid wallet at ingest time."},"spending_minimum_cents":{"type":["integer","null"],"description":"Minimum minor-unit amount applied to each pay-in-advance usage drawdown when set."},"option_values":{"type":["object","null"],"additionalProperties":{"type":"string"},"description":"The variant's options, e.g. {\"Colour\":\"Navy\",\"Size\":\"M\"}. Null when the price is not a variant."},"sku":{"type":["string","null"],"description":"The variant's SKU (what to pack). Unique per merchant among active prices."},"image_url":{"type":["string","null"],"description":"The variant's image (storage key or URL), one of the product's images."},"metadata":{"type":"object","additionalProperties":true,"description":"Custom key-value metadata"},"created_at":{"type":"string","format":"date-time","description":"Creation timestamp"},"updated_at":{"type":"string","format":"date-time","description":"Last update timestamp"}},"required":["id","object","product_id","active","currency","unit_amount","type","pricing_model","version","billing_scheme","created_at","updated_at"],"description":"A price defines how much and how often to charge for a product. A single product can have multiple prices for different currencies, intervals, or tiers.","example":{"id":"b5d8e2f1-3a6c-4d9b-8e7f-1c4a2b5d6e73","object":"price","product_id":"a7b3c9d2-1e4f-4b6a-8c5d-2f9e0a1b3c42","active":true,"currency":"gbp","unit_amount":4900,"unit_amount_decimal":"4900","type":"recurring","pricing_model":"fixed","interval":"month","interval_count":1,"trial_period_days":14,"start_rule":"immediate","minimum_amount":null,"maximum_amount":null,"suggested_amount":null,"preset_amounts":null,"name":"Monthly","nickname":"Monthly","description":null,"version":1,"lookup_key":"pro_monthly_gbp","billing_scheme":"per_unit","tiers_mode":null,"tiers":null,"transform_quantity":null,"percentage_rate_bps":null,"pay_in_advance":true,"spending_minimum_cents":null,"option_values":null,"sku":null,"image_url":null,"metadata":{},"created_at":"2026-04-02T10:00:00.000Z","updated_at":"2026-04-02T10:05:00.000Z"}},"PriceTier":{"type":"object","properties":{"up_to":{"type":["integer","null"],"exclusiveMinimum":0,"description":"Upper bound of this tier (1-based inclusive). `null` = open-ended catch-all. Only the FINAL tier may set `up_to: null`."},"unit_amount_minor":{"type":["integer","null"],"minimum":0,"description":"Per-unit charge in minor currency units (cents). May be null if the tier charges only a flat fee, or when the rate is sub-penny and carried by `unit_amount_decimal`."},"unit_amount_decimal":{"type":["string","null"],"description":"Exact per-unit rate for this tier in minor currency units, as a decimal string (sub-penny safe, e.g. \"0.0000024\"). Takes precedence over `unit_amount_minor`, which is null whenever the rate is not a whole number of minor units. Must be > 0 with at most 12 decimal places. Tier BOUNDARIES remain integer."},"flat_amount_minor":{"type":["integer","null"],"minimum":0,"description":"Flat fee charged once when quantity enters this tier. May be null if the tier charges only a per-unit amount. Always a whole number of minor units — only the per-unit RATE supports sub-penny precision."}},"required":["up_to","unit_amount_minor","flat_amount_minor"]},"ListCursorError":{"type":"object","properties":{"error":{"type":"object","properties":{"type":{"type":"string","enum":["invalid_request_error"]},"code":{"type":"string","enum":["parameter_invalid","resource_missing"],"description":"`parameter_invalid` when both cursors are supplied; `resource_missing` when the cursor names no object of the caller's merchant."},"message":{"type":"string"},"param":{"type":"string","enum":["starting_after","ending_before"],"description":"The cursor parameter the error is about."},"request_id":{"type":"string"}},"required":["type","code","message","param"]}},"required":["error"],"description":"Returned when cursor pagination parameters are rejected: either `starting_after` and `ending_before` were combined, or a cursor names no object of the caller's merchant."},"PriceErrorResponse":{"type":"object","properties":{"error":{"type":"object","properties":{"type":{"type":"string"},"code":{"type":"string"},"message":{"type":"string"},"param":{"type":"string"}},"required":["type","code","message"]}},"required":["error"],"description":"Error response returned when a price operation fails."},"CreatePriceRequest":{"type":"object","properties":{"product_id":{"type":"string","format":"uuid","description":"ID of the product this price belongs to"},"currency":{"type":"string","minLength":3,"maxLength":3,"description":"Three-letter ISO currency code"},"unit_amount":{"type":"integer","minimum":0,"description":"Price in minor units (cents). Required for fixed pricing."},"type":{"type":"string","enum":["one_time","recurring"],"default":"one_time","description":"Price type"},"pricing_model":{"type":"string","enum":["fixed","pay_what_you_want","free"],"default":"fixed","description":"Pricing model"},"interval":{"type":"string","enum":["day","week","month","year"],"description":"Billing interval (required for recurring)"},"interval_count":{"type":"integer","minimum":1,"maximum":12,"description":"Number of intervals between billings (1-12)"},"trial_period_days":{"type":"integer","minimum":0,"maximum":730,"description":"Trial period in days (0-730)"},"start_rule":{"type":"string","enum":["immediate","next_anchor","prorate"],"description":"First-charge behavior for recurring checkout. `immediate`: full first period now. `next_anchor`: first charge on the billing anchor. `prorate`: prorated stub now, then full charge on the billing anchor."},"minimum_amount":{"type":"integer","minimum":0,"description":"PWYW: minimum amount in cents"},"maximum_amount":{"type":"integer","minimum":0,"description":"PWYW: maximum amount in cents"},"suggested_amount":{"type":"integer","minimum":0,"description":"PWYW: suggested amount in cents"},"preset_amounts":{"type":"array","items":{"type":"integer","minimum":0},"maxItems":10,"description":"PWYW: quick-select amounts (max 10)"},"name":{"type":"string","maxLength":100,"description":"Customer-facing price name (e.g., 'Morning', 'Full day')"},"nickname":{"type":"string","maxLength":100,"description":"Display name (e.g., 'Monthly', 'Annual')"},"description":{"type":"string","maxLength":500,"description":"Description of this price option"},"lookup_key":{"type":"string","maxLength":100,"description":"Stable key for API lookups"},"billing_scheme":{"type":"string","enum":["per_unit","tiered","percentage"],"description":"Billing scheme. Defaults to `per_unit`. Set to `tiered` with `tiers_mode` + `tiers` for graduated or volume pricing."},"tiers_mode":{"type":"string","enum":["graduated","volume"],"description":"Tiered pricing mode. Required when `billing_scheme` is `tiered`, must be omitted otherwise."},"tiers":{"type":"array","items":{"$ref":"#/components/schemas/PriceTier"},"minItems":2,"maxItems":50,"description":"Price tiers (ordered by `up_to`, min 2, max 50). Required when `billing_scheme` is `tiered`. Final tier MUST have `up_to: null`."},"transform_quantity":{"$ref":"#/components/schemas/TransformQuantity"},"percentage_rate_bps":{"type":"integer","minimum":0,"maximum":10000,"description":"Rate in basis points. 70 = 0.7%. Required when `billing_scheme` is `percentage`. Range 0–10000 (0%–100%).","example":70},"metadata":{"type":"object","additionalProperties":true,"description":"Custom key-value metadata"}},"required":["product_id"],"description":"Parameters for creating a new price. Specify the product, amount, currency, and billing interval."},"TransformQuantity":{"type":"object","properties":{"divide_by":{"type":"integer","exclusiveMinimum":0,"description":"Package size — quantity is divided by this value before pricing. Must be >= 1. Example: `divide_by: 10` with `round: up` charges per pack of 10."},"round":{"type":"string","enum":["up","down"],"description":"Rounding mode when quantity is not an exact multiple of `divide_by`."}},"required":["divide_by","round"],"description":"Optional quantity transform (package pricing) applied before tier lookup."},"UpdatePriceRequest":{"type":"object","properties":{"active":{"type":"boolean","description":"Whether the price is active"},"name":{"type":["string","null"],"maxLength":100,"description":"Customer-facing price name"},"nickname":{"type":["string","null"],"maxLength":100,"description":"Display name"},"description":{"type":["string","null"],"maxLength":500,"description":"Description of this price option"},"lookup_key":{"type":["string","null"],"maxLength":100,"description":"Stable key for API lookups"},"metadata":{"type":"object","additionalProperties":true,"description":"Custom key-value metadata"}},"description":"Parameters for updating an existing price. Amounts cannot be changed — archive and create a new price instead."},"MeterListResponse":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","additionalProperties":{}},"description":"Array of meter objects","example":[{"id":"7c8d9e0f-1a2b-4630-8d4e-5f6a7b8c9d01","name":"API calls","slug":"api-calls","description":"Counts requests to the merchant's public API.","event_name":"api_call","aggregation":"count","value_key":null,"value_expression":null,"meter_definition_version":1,"unit_name":"calls","unit_of_measure_code":"C62","status":"active","filter_conditions":null,"unique_count_key":null,"carry_forward":false,"metadata":{},"created_at":"2026-04-02T10:00:00.000Z","updated_at":"2026-04-02T10:05:00.000Z"}]},"pagination":{"type":"object","properties":{"limit":{"type":"number","example":50},"offset":{"type":"number","example":0},"count":{"type":"number","example":1}},"required":["limit","offset","count"]}},"required":["data","pagination"],"description":"A list of meters for the merchant."},"MeterQuantitiesResponse":{"type":"object","properties":{"object":{"type":"string","enum":["meter_quantities"]},"meter_id":{"type":"string"},"start_timestamp":{"type":"string","format":"date-time"},"end_timestamp":{"type":"string","format":"date-time"},"interval":{"type":"string","enum":["hour","day","week","month","year"]},"quantities":{"type":"array","items":{"type":"object","properties":{"timestamp":{"type":"string","format":"date-time"},"quantity":{"type":"number"}},"required":["timestamp","quantity"]}}},"required":["object","meter_id","start_timestamp","end_timestamp","interval","quantities"],"description":"Bucketed meter quantities over the requested window.","example":{"object":"meter_quantities","meter_id":"7c8d9e0f-1a2b-4630-8d4e-5f6a7b8c9d01","start_timestamp":"2026-04-16T00:00:00.000Z","end_timestamp":"2026-04-19T00:00:00.000Z","interval":"day","quantities":[{"timestamp":"2026-04-16T00:00:00.000Z","quantity":412},{"timestamp":"2026-04-17T00:00:00.000Z","quantity":398},{"timestamp":"2026-04-18T00:00:00.000Z","quantity":440}]}},"MeterQuantitiesValidationError":{"type":"object","properties":{"error":{"anyOf":[{"type":"object","properties":{"type":{"type":"string","enum":["invalid_request_error"]},"code":{"type":"string","enum":["parameter_invalid"]},"message":{"type":"string"},"request_id":{"type":"string"},"param":{"type":"string","enum":["id","customer_id","subscription_id"],"description":"The query parameter that was rejected."}},"required":["type","code","message","param"]},{"$ref":"#/components/schemas/MeterQuantitiesRangeError"},{"type":"string"}]}},"required":["error"],"description":"A parameter failed validation (`param` names it), or the requested range is missing or invalid."},"MeterQuantitiesRangeError":{"type":"object","properties":{"message":{"type":"string"},"type":{"type":"string"}},"required":["message","type"]},"MeterError":{"type":"object","properties":{"error":{"anyOf":[{"type":"string"},{"type":"object","properties":{"message":{"type":"string"},"type":{"type":"string"}},"required":["message","type"]}]}},"required":["error"],"description":"Error response returned when a meter operation fails."},"MeterGetResponse":{"type":"object","properties":{"data":{"type":"object","additionalProperties":{},"description":"Meter object","example":{"id":"7c8d9e0f-1a2b-4630-8d4e-5f6a7b8c9d01","name":"API calls","slug":"api-calls","description":"Counts requests to the merchant's public API.","event_name":"api_call","aggregation":"count","value_key":null,"value_expression":null,"meter_definition_version":1,"unit_name":"calls","unit_of_measure_code":"C62","status":"active","filter_conditions":null,"unique_count_key":null,"carry_forward":false,"metadata":{},"created_at":"2026-04-02T10:00:00.000Z","updated_at":"2026-04-02T10:05:00.000Z"}}},"required":["data"],"description":"A single meter object with configuration details."},"CreateMeterResponse":{"type":"object","properties":{"data":{"type":"object","additionalProperties":{},"description":"Created meter object","example":{"id":"7c8d9e0f-1a2b-4630-8d4e-5f6a7b8c9d01","name":"API calls","slug":"api-calls","description":"Counts requests to the merchant's public API.","event_name":"api_call","aggregation":"count","value_key":null,"value_expression":null,"meter_definition_version":1,"unit_name":"calls","unit_of_measure_code":"C62","status":"active","filter_conditions":null,"unique_count_key":null,"carry_forward":false,"metadata":{},"created_at":"2026-04-02T10:00:00.000Z","updated_at":"2026-04-02T10:05:00.000Z"}}},"required":["data"],"description":"The newly created meter object."},"CreateMeterRequest":{"type":"object","properties":{"name":{"type":"string","description":"Human-readable meter name","example":"API Calls"},"event_name":{"type":"string","description":"Event name to match against","example":"api_call"},"aggregation":{"type":"string","enum":["sum","count","count_unique","max","last"],"default":"count","description":"Aggregation method","example":"count"},"slug":{"type":"string","description":"URL-safe identifier","example":"api-calls"},"description":{"type":"string","description":"Meter description"},"value_key":{"type":"string","description":"Property key for sum/max/last aggregations","example":"tokens"},"value_expression":{"type":"string","description":"Constrained arithmetic expression over numeric event properties. Example: input_tokens + output_tokens","example":"input_tokens + output_tokens"},"filter_conditions":{"type":"array","items":{"type":"object","additionalProperties":{}},"description":"Filter conditions for matching events"},"unique_count_key":{"type":"string","description":"Property key for count_unique aggregation"},"unit_name":{"type":"string","description":"Display unit name","example":"calls"},"unit_of_measure_code":{"type":"string","enum":["C62","E48","HUR","DAY","MON","ANN","GB","MB","KGM","LTR"],"description":"UN/ECE Rec 20 code. Omitted => C62. unit_name remains the display label.","example":"C62"},"carry_forward":{"type":"boolean","description":"Carry forward last value across periods"},"metadata":{"type":"object","additionalProperties":{},"description":"Arbitrary key-value metadata"},"alert_thresholds":{"type":"array","items":{"type":"number","minimum":1,"maximum":200},"maxItems":5,"description":"Threshold percentages that trigger usage.threshold.reached webhooks.","example":[50,80,100]}},"required":["name","event_name"],"description":"Parameters for creating a new usage meter with aggregation rules and event matching."},"UpdateMeterResponse":{"type":"object","properties":{"data":{"type":"object","additionalProperties":{},"description":"Updated meter object","example":{"id":"7c8d9e0f-1a2b-4630-8d4e-5f6a7b8c9d01","name":"API calls","slug":"api-calls","description":"Counts requests to the merchant's public API.","event_name":"api_call","aggregation":"count","value_key":null,"value_expression":null,"meter_definition_version":1,"unit_name":"calls","unit_of_measure_code":"C62","status":"active","filter_conditions":null,"unique_count_key":null,"carry_forward":false,"metadata":{},"created_at":"2026-04-02T10:00:00.000Z","updated_at":"2026-04-02T10:05:00.000Z"}}},"required":["data"],"description":"The updated meter object reflecting the changes."},"UpdateMeterRequest":{"type":"object","properties":{"name":{"type":"string","description":"Updated meter name"},"slug":{"type":"string","description":"Updated slug"},"description":{"type":"string","description":"Updated description"},"status":{"type":"string","enum":["active","archived","draft"],"description":"Meter status"},"value_key":{"type":"string","description":"Updated value key"},"value_expression":{"type":["string","null"],"description":"Updated constrained arithmetic expression over numeric event properties. null clears it."},"filter_conditions":{"type":"array","items":{"type":"object","additionalProperties":{}},"description":"Updated filter conditions"},"unique_count_key":{"type":"string","description":"Updated unique count key"},"unit_name":{"type":"string","description":"Updated unit name"},"unit_of_measure_code":{"type":"string","enum":["C62","E48","HUR","DAY","MON","ANN","GB","MB","KGM","LTR"],"description":"Updated UN/ECE Rec 20 code. Does not rewrite historical match/line snapshots.","example":"C62"},"carry_forward":{"type":"boolean","description":"Updated carry forward setting"},"metadata":{"type":"object","additionalProperties":{},"description":"Updated metadata"},"alert_thresholds":{"type":"array","items":{"type":"number","minimum":1,"maximum":200},"maxItems":5,"description":"Updated threshold percentages for usage.threshold.reached webhooks.","example":[50,80,100]}},"description":"Parameters for updating an existing meter. Only display name and metadata can be changed."},"MeterPriceResponse":{"type":"object","properties":{"data":{"type":"object","additionalProperties":{},"description":"Price object with tiers"}},"required":["data"],"description":"A single meter price with per-unit cost and billing configuration."},"MeterPriceError":{"type":"object","properties":{"error":{"anyOf":[{"type":"string"},{"type":"object","properties":{"message":{"type":"string"},"type":{"type":"string"}},"required":["message","type"]}]}},"required":["error"],"description":"Error response returned when a meter price operation fails."},"CreateMeterPriceRequest":{"type":"object","properties":{"pricing_model":{"type":"string","enum":["per_unit","graduated","volume","package","percentage"],"description":"Pricing model. 'percentage' charges a basis-points percentage of the metered amount (REV-3465); other models charge per unit, graduated tiers, volume tiers, or packages.","example":"per_unit"},"currency":{"type":"string","minLength":3,"maxLength":3,"description":"ISO 4217 currency code. A usage-based subscription has one currency; metered events attributed to it are assumed to be in this currency and are not FX-converted by RevKeen.","example":"GBP"},"unit_amount_minor":{"type":"integer","minimum":0,"description":"Price per unit in minor units (for per_unit). For a sub-penny rate use `unit_amount_decimal`.","example":100},"unit_amount_decimal":{"type":"string","description":"Exact per-unit rate in minor units as a decimal string (sub-penny safe, e.g. \"0.02\" = 0.02 pence). Takes precedence over `unit_amount_minor`. Mirrors Stripe's `unit_amount_decimal`.","example":"0.02"},"flat_fee_minor":{"type":"integer","minimum":0,"description":"Base charge per period in minor units","example":0},"package_size":{"type":"integer","minimum":1,"description":"Units per package (for package model)","example":100},"cost_per_unit_minor":{"type":"integer","minimum":0,"description":"Merchant's cost per unit for margin intelligence","example":5},"name":{"type":"string","maxLength":100,"description":"Customer-facing price name","example":"Full day"},"nickname":{"type":"string","maxLength":100,"description":"Internal price nickname","example":"holiday-club-full-day"},"description":{"type":"string","maxLength":500,"description":"Description of this price option","example":"Full-day holiday club session"},"tiers":{"type":"array","items":{"$ref":"#/components/schemas/MeterPriceTierInput"},"description":"Tier configuration (for graduated and volume)"},"filter_conditions":{"type":"array","items":{"type":"object","additionalProperties":{}},"description":"Optional event property predicates for dimensional pricing. Empty or omitted = base/fallthrough price."},"filter_priority":{"type":"integer","description":"Tie-breaker when multiple matching filters have the same specificity.","example":10},"pay_in_advance":{"type":"boolean","description":"When true, matching usage events debit the customer's prepaid wallet at ingest time.","example":true},"spending_minimum_cents":{"type":"integer","minimum":0,"description":"Minimum minor-unit amount applied to each pay-in-advance usage drawdown.","example":50},"percentage_rate_bps":{"type":"integer","minimum":0,"maximum":10000,"description":"Rate in basis points. 70 = 0.7%. Required when pricing_model='percentage'. Range 0–10000 (0%–100%).","example":70}},"required":["pricing_model","currency"],"description":"Parameters for creating a price attached to a usage meter, defining per-unit cost, tiered tiers, or a percentage rate."},"MeterPriceTierInput":{"type":"object","properties":{"up_to":{"type":["integer","null"],"minimum":1,"description":"Upper bound of this tier (1-based inclusive). null = open-ended final tier. New clients should use this canonical shape; legacy first_unit/last_unit is accepted during the deprecation window.","example":1000},"unit_amount_minor":{"type":"integer","minimum":0,"description":"Per-unit price in this tier (minor units). For a sub-penny per-tier rate use `unit_amount_decimal`. Optional when `unit_amount_decimal` or `flat_amount_minor` is supplied.","example":10},"unit_amount_decimal":{"type":"string","description":"Exact per-unit rate for this tier in minor units as a decimal string (sub-penny safe, e.g. \"0.0000024\" = 0.0000024 pence per token). Takes precedence over `unit_amount_minor`. Must be > 0 with at most 12 decimal places. Amounts are rounded per tier with half-up (away from zero) before the tier subtotals are summed.","example":"0.0000024"},"flat_amount_minor":{"type":"integer","minimum":0,"description":"Flat fee for entering this tier (minor units). Always a whole number of minor units — only the per-unit RATE supports sub-penny precision.","example":0}},"additionalProperties":{}},"MeterPriceListResponse":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","additionalProperties":{}},"description":"Array of price objects"}},"required":["data"],"description":"A list of prices attached to a meter."},"UpdateMeterPriceRequest":{"type":"object","properties":{"unit_amount_minor":{"type":"integer","minimum":0},"unit_amount_decimal":{"type":"string"},"flat_fee_minor":{"type":"integer","minimum":0},"package_size":{"type":"integer","minimum":1},"cost_per_unit_minor":{"type":"integer","minimum":0},"name":{"type":["string","null"],"maxLength":100},"nickname":{"type":["string","null"],"maxLength":100},"description":{"type":["string","null"],"maxLength":500},"tiers":{"type":"array","items":{"$ref":"#/components/schemas/MeterPriceTierInput"}},"filter_conditions":{"type":"array","items":{"type":"object","additionalProperties":{}}},"filter_priority":{"type":"integer"},"pay_in_advance":{"type":"boolean"},"spending_minimum_cents":{"type":["integer","null"],"minimum":0},"percentage_rate_bps":{"type":"integer","minimum":0,"maximum":10000,"description":"Rate in basis points. 70 = 0.7%. Only meaningful when the price's pricing_model is 'percentage'.","example":70}},"description":"Parameters for updating a meter price. Active prices on existing subscriptions are not retroactively changed."},"InvoiceLineUsageEventsList":{"type":"object","properties":{"object":{"type":"string","enum":["list"]},"data":{"type":"array","items":{"$ref":"#/components/schemas/InvoiceLineUsageEventProvenanceItem"}},"has_more":{"type":"boolean"},"url":{"type":"string"},"line":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"invoice_id":{"type":"string","format":"uuid"},"meter_id":{"type":["string","null"],"format":"uuid"},"usage_record_id":{"type":["string","null"],"format":"uuid"},"unit_amount_minor":{"type":"integer"},"total_amount_minor":{"type":"integer"},"currency":{"type":"string"},"usage_quantity":{"type":["string","null"]},"billed_amount_minor_sum":{"type":"integer"}},"required":["id","invoice_id","meter_id","usage_record_id","unit_amount_minor","total_amount_minor","currency","usage_quantity","billed_amount_minor_sum"]}},"required":["data","has_more","line"]},"InvoiceLineUsageEventProvenanceItem":{"type":"object","properties":{"outcome":{"type":"string","enum":["matched","excluded"]},"exclusion_reason":{"type":["string","null"]},"entry":{"type":["object","null"],"properties":{"id":{"type":"string","format":"uuid"},"amount_minor":{"type":"integer"},"precise_amount_minor":{"type":["string","null"],"description":"Unrounded amount in minor units as a decimal string (REV-6211). Not integer cents; sub-penny rates stay exact. Null means not computable for this pricing scheme; never coerce to 0."},"quantity":{"type":"string"},"currency":{"type":["string","null"]},"rating_version":{"type":"integer"},"price_id":{"type":"string","format":"uuid"},"price_terms_hash":{"type":["string","null"]}},"required":["id","amount_minor","precise_amount_minor","quantity","currency","rating_version","price_id","price_terms_hash"]},"match":{"type":["object","null"],"properties":{"id":{"type":"string","format":"uuid"},"meter_id":{"type":"string","format":"uuid"},"meter_definition_version":{"type":"integer"},"aggregation":{"type":"string"},"resolved_quantity":{"type":"string"},"unit_of_measure_code":{"type":["string","null"]}},"required":["id","meter_id","meter_definition_version","aggregation","resolved_quantity","unit_of_measure_code"]},"event":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"event_name":{"type":["string","null"]},"event_time":{"type":["string","null"],"format":"date-time"},"quantity":{"type":"string"},"idempotency_key":{"type":["string","null"]}},"required":["id","event_name","event_time","quantity","idempotency_key"]}},"required":["outcome","event"]},"InvoiceLineUsageEventsError":{"type":"object","properties":{"error":{"anyOf":[{"type":"string"},{"type":"object","properties":{"message":{"type":"string"},"type":{"type":"string"}},"required":["message","type"]}]}},"required":["error"]},"OrderListResponse":{"type":"object","properties":{"object":{"type":"string","enum":["list"]},"data":{"type":"array","items":{"$ref":"#/components/schemas/Order"}},"has_more":{"type":"boolean"},"total_count":{"type":"integer"}},"required":["object","data","has_more"],"description":"A paginated list of orders matching the query filters."},"Order":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Unique identifier for the order"},"object":{"type":"string","enum":["order"],"description":"Object type"},"public_id":{"type":"string","description":"Public-facing order ID"},"customer_id":{"type":["string","null"],"format":"uuid","description":"ID of the customer"},"status":{"type":"string","enum":["draft","pending","paid","partially_paid","refunded","canceled","fulfilled"],"description":"Order status. draft: Being built. pending: Awaiting payment. paid: Fully paid. partially_paid: Partial payment received. refunded: Refunded. canceled: Canceled. fulfilled: Shipped/delivered."},"billing_type":{"type":"string","enum":["one_time","recurring"],"description":"Billing type. one_time: Single purchase. recurring: Subscription-based."},"fulfillment_status":{"type":["string","null"],"enum":["unfulfilled","partial","fulfilled","returned",null],"description":"Fulfillment status for physical goods."},"currency":{"type":"string","description":"Three-letter ISO currency code"},"subtotal":{"type":"integer","description":"Subtotal in cents"},"discount":{"type":"integer","description":"Total discount in cents"},"tax":{"type":"integer","description":"Total tax in cents"},"total":{"type":"integer","description":"Total amount in cents"},"amount_paid":{"type":"integer","description":"Amount paid in cents"},"amount_refunded":{"type":"integer","description":"Amount refunded in cents"},"amount_due":{"type":"integer","description":"Amount still due in cents"},"line_items":{"type":"array","items":{"$ref":"#/components/schemas/OrderLineItem"}},"metadata":{"type":"object","additionalProperties":true,"description":"Custom key-value metadata"},"notes":{"type":["string","null"]},"invoice_id":{"type":["string","null"],"format":"uuid","description":"ID of the linked invoice (Commercial Truth)"},"subscription_id":{"type":["string","null"],"format":"uuid","description":"ID of the linked subscription (for recurring fulfillment)"},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"},"paid_at":{"type":["string","null"],"format":"date-time"},"canceled_at":{"type":["string","null"],"format":"date-time"},"fulfilled_at":{"type":["string","null"],"format":"date-time"},"expires_at":{"type":["string","null"],"format":"date-time"}},"required":["id","object","public_id","status","billing_type","currency","subtotal","discount","tax","total","amount_paid","amount_refunded","amount_due","created_at","updated_at"],"description":"An order represents a one-off purchase, created when a checkout is fulfilled, with lifecycle operations: update, cancel, and fulfill."},"OrderLineItem":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"product_id":{"type":["string","null"],"format":"uuid"},"price_id":{"type":["string","null"],"format":"uuid","description":"ID of the price this line sold. For a product with variants, the price is the variant; null for lines sold without a price"},"sku":{"type":["string","null"],"description":"SKU of the price this line sold, read from the price; null when the price has no SKU or the line has no price"},"description":{"type":"string"},"quantity":{"type":"integer","minimum":1},"unit_price":{"type":"integer","description":"Unit price in cents"},"subtotal":{"type":"integer","description":"Subtotal in cents (unit_price * quantity)"},"discount":{"type":"integer","default":0,"description":"Discount in cents"},"tax":{"type":"integer","default":0,"description":"Tax in cents"},"total":{"type":"integer","description":"Total in cents (subtotal - discount + tax)"},"metadata":{"type":"object","additionalProperties":true,"description":"Custom key-value metadata"},"position":{"type":"integer","default":0},"fulfillment_type":{"type":"string","enum":["none","physical","digital"],"description":"How this line is fulfilled"},"fulfillment_status":{"type":"string","enum":["pending","processing","shipped","delivered","cancelled"],"description":"Fulfilment state of this line; processing means partly shipped"},"fulfilled_quantity":{"type":"integer","minimum":0,"description":"Units of this line shipped so far"},"tracking_number":{"type":["string","null"]},"tracking_url":{"type":["string","null"]},"carrier":{"type":["string","null"]},"shipped_at":{"type":["string","null"],"format":"date-time"},"delivered_at":{"type":["string","null"],"format":"date-time"}},"required":["id","description","quantity","unit_price","subtotal","total"],"description":"A line item within an order, representing a product, quantity, and unit price."},"OrderErrorResponse":{"type":"object","properties":{"error":{"type":"object","properties":{"type":{"type":"string"},"code":{"type":"string"},"message":{"type":"string"},"param":{"type":"string"}},"required":["type","code","message"]}},"required":["error"],"description":"Error response returned when an order operation fails."},"UpdateOrderRequest":{"type":"object","properties":{"customer_id":{"type":["string","null"],"format":"uuid"},"metadata":{"type":"object","additionalProperties":true,"description":"Custom key-value metadata"},"notes":{"type":["string","null"],"maxLength":1000}},"description":"Parameters for updating order metadata or notes before fulfillment."},"CancelOrderRequest":{"type":"object","properties":{"reason":{"type":"string","maxLength":500,"description":"Reason for cancellation"}},"description":"Parameters for canceling an order before fulfillment."},"FulfillOrderRequest":{"type":"object","properties":{"tracking_number":{"type":"string","maxLength":100,"description":"Shipping tracking number"},"carrier":{"type":"string","maxLength":50,"description":"Shipping carrier (e.g., 'ups', 'fedex', 'usps')"},"line_item_ids":{"type":"array","items":{"type":"string","format":"uuid"},"maxItems":500,"description":"Specific line items to fulfill, each in full (defaults to every unfulfilled line)"},"tracking_url":{"type":"string","maxLength":2000,"format":"uri","description":"Carrier tracking link"},"status":{"type":"string","enum":["shipped","delivered"],"default":"shipped","description":"Mark the lines shipped (default) or delivered"},"notify_customer":{"type":"boolean","default":true,"description":"Send fulfillment notification to customer"}},"description":"Parameters for marking an order as fulfilled, with optional tracking and shipping details."},"PaymentListResponse":{"type":"object","properties":{"object":{"type":"string","enum":["list"]},"data":{"type":"array","items":{"$ref":"#/components/schemas/Payment"}},"has_more":{"type":"boolean"},"total_count":{"type":"integer"}},"required":["object","data","has_more"],"description":"A paginated list of payments matching the query filters."},"Payment":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Unique identifier for the payment"},"object":{"type":"string","enum":["payment"],"description":"Object type"},"invoice_id":{"type":["string","null"],"format":"uuid","description":"Associated invoice ID"},"customer_id":{"type":["string","null"],"format":"uuid","description":"Customer ID"},"status":{"type":"string","enum":["pending","authorized","captured","succeeded","failed","voided","refunded","partially_refunded"],"description":"Payment status. pending: Processing. authorized: Funds reserved. captured/succeeded: Funds collected. failed: Payment failed. voided: Canceled before capture. refunded/partially_refunded: Returned to customer."},"currency":{"type":"string","description":"Three-letter ISO currency code"},"amount":{"type":"integer","description":"Payment amount in cents"},"amount_captured":{"type":["integer","null"],"description":"Amount captured in cents (for auth-capture flow)"},"amount_refunded":{"type":"integer","default":0,"description":"Amount refunded in cents"},"gateway":{"type":["string","null"],"description":"Name of the payment processor that handled this payment"},"gateway_transaction_id":{"type":["string","null"],"description":"Processor's transaction reference"},"gateway_response_code":{"type":["string","null"],"description":"Normalized response code from the processor"},"gateway_response_text":{"type":["string","null"],"description":"Human-readable response message from the processor"},"payment_method_type":{"type":["string","null"],"description":"Payment method type (card, ach, wallet)"},"card_brand":{"type":["string","null"],"description":"Card brand (visa, mastercard, etc.)"},"card_last_four":{"type":["string","null"],"description":"Last 4 digits of card"},"payment_channel":{"type":["string","null"],"enum":["card_present","card_not_present","bank_transfer","manual",null],"description":"Payment channel. card_present: terminal/POS payment. card_not_present: online or recurring payment. bank_transfer: ACH/direct debit. manual: manually recorded."},"entry_mode":{"type":["string","null"],"enum":["emv_chip","contactless","magnetic_stripe","manual_entry","fallback",null],"description":"Card entry mode for card-present (terminal) payments. null for online payments."},"metadata":{"type":"object","additionalProperties":true,"description":"Custom key-value metadata"},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"},"authorized_at":{"type":["string","null"],"format":"date-time"},"captured_at":{"type":["string","null"],"format":"date-time"},"voided_at":{"type":["string","null"],"format":"date-time"},"refunded_at":{"type":["string","null"],"format":"date-time"}},"required":["id","object","status","currency","amount","created_at","updated_at"],"description":"A completed or in-progress money movement. Payments represent funds transferred between a customer and your account across all channels — online, terminal, and recurring."},"PaymentErrorResponse":{"type":"object","properties":{"error":{"type":"object","properties":{"type":{"type":"string"},"code":{"type":"string"},"message":{"type":"string"},"param":{"type":"string"}},"required":["type","code","message"]}},"required":["error"],"description":"Error response returned when a payment operation fails."},"CapturePaymentRequest":{"type":"object","properties":{"amount":{"type":"integer","minimum":1,"description":"Amount to capture in cents. Defaults to full authorized amount. Must be <= authorized amount."}},"description":"Parameters for capturing a previously authorized payment. Optionally specify a partial capture amount."},"CancelPaymentRequest":{"type":"object","properties":{"reason":{"type":"string","maxLength":500,"description":"Reason for cancellation/void"}},"description":"Parameters for voiding a payment before settlement. Include a reason for your records."},"AccountingInvoicePaymentRequest":{"type":"object","properties":{"id":{"type":"string","format":"uuid","example":"2b3c4d5e-6f7a-4685-ac93-0e1f2a3b4c56"},"object":{"type":"string","enum":["accounting_invoice_payment_request"]},"merchant_id":{"type":"string","format":"uuid","example":"8f14e45f-ceea-4e7a-9b4b-7c3f1a2d5e60"},"provider":{"type":"string"},"connection_id":{"type":"string","format":"uuid"},"provider_account_id":{"type":"string"},"external_invoice_id":{"type":"string"},"external_invoice_number":{"type":["string","null"]},"external_invoice_status":{"type":["string","null"]},"external_invoice_url":{"type":["string","null"]},"external_invoice_issued_at":{"type":["string","null"],"format":"date-time"},"external_invoice_due_at":{"type":["string","null"],"format":"date-time"},"external_invoice_updated_at":{"type":["string","null"],"format":"date-time"},"external_customer_id":{"type":["string","null"]},"external_customer_reference":{"type":["string","null"]},"external_customer_name":{"type":["string","null"]},"external_customer_email":{"type":["string","null"]},"amount_due_minor":{"type":"integer","example":12000},"total_amount_minor":{"type":["integer","null"]},"amount_paid_minor":{"type":["integer","null"]},"currency":{"type":"string","example":"GBP"},"checkout_session_id":{"type":["string","null"],"format":"uuid"},"checkout_url":{"type":["string","null"]},"checkout_expires_at":{"type":["string","null"],"format":"date-time"},"checkout_success_url":{"type":["string","null"]},"checkout_cancel_url":{"type":["string","null"]},"checkout_allowed_methods":{"type":["array","null"],"items":{"type":"string"}},"status":{"type":"string","example":"checkout_created"},"sync_status":{"type":"string"},"idempotency_key":{"type":"string"},"payload_fingerprint":{"type":"string"},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"}},"required":["id","object","merchant_id","provider","connection_id","provider_account_id","external_invoice_id","amount_due_minor","currency","status","sync_status","idempotency_key","payload_fingerprint","created_at","updated_at"],"example":{"id":"2b3c4d5e-6f7a-4685-ac93-0e1f2a3b4c56","object":"accounting_invoice_payment_request","merchant_id":"8f14e45f-ceea-4e7a-9b4b-7c3f1a2d5e60","provider":"xero","connection_id":"1a2b3c4d-5e6f-4674-9b82-9d0e1f2a3b45","provider_account_id":"example-tenant-0001","external_invoice_id":"example-invoice-0001","external_invoice_number":"ACC-1001","external_invoice_status":"AUTHORISED","external_invoice_url":"https://example.com/accounting/invoices/ACC-1001","external_invoice_issued_at":"2026-04-20T09:00:00.000Z","external_invoice_due_at":"2026-05-20T09:00:00.000Z","external_invoice_updated_at":"2026-04-20T09:00:00.000Z","external_customer_id":"example-contact-0001","external_customer_reference":"CRM-10482","external_customer_name":"Analytical Engines Ltd","external_customer_email":"accounts@example.com","amount_due_minor":12000,"total_amount_minor":12000,"amount_paid_minor":0,"currency":"GBP","checkout_session_id":"3c4d5e6f-7a8b-4696-8da4-1f2a3b4c5d67","checkout_url":"https://example.com/checkout/3c4d5e6f-7a8b-4696-8da4-1f2a3b4c5d67","checkout_expires_at":"2026-04-21T09:00:00.000Z","checkout_success_url":"https://example.com/invoices/paid","checkout_cancel_url":"https://example.com/invoices/cancelled","checkout_allowed_methods":["card"],"status":"checkout_created","sync_status":"not_started","idempotency_key":"acc-1001-payment-request","payload_fingerprint":"3f2a9c7e1b4d8a60","created_at":"2026-04-20T09:00:05.000Z","updated_at":"2026-04-20T09:00:06.000Z"}},"CreateAccountingInvoicePaymentRequestInput":{"type":"object","properties":{"provider":{"type":"string","enum":["quickbooks_online","xero"]},"connection_id":{"type":"string","format":"uuid"},"provider_account_id":{"type":"string","minLength":1},"external_invoice":{"type":"object","properties":{"id":{"type":"string","minLength":1},"number":{"type":["string","null"]},"status":{"type":["string","null"]},"url":{"type":["string","null"],"format":"uri"},"issued_at":{"type":["string","null"],"format":"date-time"},"due_at":{"type":["string","null"],"format":"date-time"},"updated_at":{"type":["string","null"],"format":"date-time"}},"required":["id"]},"external_customer":{"type":["object","null"],"properties":{"id":{"type":["string","null"]},"reference":{"type":["string","null"]},"name":{"type":["string","null"]},"email":{"type":["string","null"],"format":"email"}}},"amount_due_minor":{"type":"integer","exclusiveMinimum":0},"total_amount_minor":{"type":["integer","null"],"minimum":0},"amount_paid_minor":{"type":["integer","null"],"minimum":0},"currency":{"type":"string","minLength":3,"maxLength":3},"checkout":{"type":["object","null"],"properties":{"success_url":{"type":["string","null"],"format":"uri"},"cancel_url":{"type":["string","null"],"format":"uri"},"allowed_methods":{"type":["array","null"],"items":{"type":"string","enum":["card","direct_debit","in_store"]}}}},"payload_fingerprint":{"type":["string","null"],"minLength":8},"provider_metadata":{"type":["object","null"],"additionalProperties":{}},"safe_provider_invoice_snapshot":{"type":["object","null"],"additionalProperties":{}}},"required":["provider","connection_id","provider_account_id","external_invoice","amount_due_minor","currency"],"description":"Create an accounting-led invoice payment request and mint/reuse a checkout session."},"WebhookEndpoint":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Unique identifier for the webhook endpoint","example":"1b2c3d4e-5f6a-4b7c-9d8e-0f1a2b3c4d98"},"url":{"type":"string","format":"uri","description":"The URL that receives webhook POST requests","example":"https://example.com/webhooks/revkeen"},"description":{"type":["string","null"],"description":"Optional human-readable description","example":"Production webhook endpoint"},"enabled_events":{"type":"array","items":{"type":"string"},"description":"Event types this endpoint subscribes to","example":["invoice.paid","subscription.created"]},"status":{"type":"string","enum":["enabled","disabled"],"description":"Whether the endpoint is currently receiving events","example":"enabled"},"secret":{"type":"string","description":"Signing secret. Returned only on create or secret rotation.","example":"whsec_…"},"circuit_breaker_state":{"type":"string","enum":["closed","open","half_open"],"description":"Circuit breaker status. Opens after repeated delivery failures.","example":"closed"},"total_deliveries":{"type":"number","description":"Total number of delivery attempts","example":12},"successful_deliveries":{"type":"number","description":"Number of successful deliveries","example":12},"failed_deliveries":{"type":"number","description":"Number of failed deliveries","example":0},"last_delivery_at":{"type":["string","null"],"format":"date-time","description":"Timestamp of the most recent delivery attempt","example":"2026-04-02T11:30:00.000Z"},"created_at":{"type":"string","format":"date-time","description":"When the endpoint was created","example":"2026-04-02T10:00:00.000Z"},"updated_at":{"type":"string","format":"date-time","description":"When the endpoint was last modified","example":"2026-04-02T10:05:00.000Z"}},"required":["id","url","description","enabled_events","status","circuit_breaker_state","total_deliveries","successful_deliveries","failed_deliveries","last_delivery_at","created_at","updated_at"],"example":{"id":"1b2c3d4e-5f6a-4b7c-9d8e-0f1a2b3c4d98","url":"https://example.com/webhooks/revkeen","description":"Production webhook endpoint","enabled_events":["invoice.paid","subscription.created"],"status":"enabled","circuit_breaker_state":"closed","total_deliveries":12,"successful_deliveries":12,"failed_deliveries":0,"last_delivery_at":"2026-04-02T11:30:00.000Z","created_at":"2026-04-02T10:00:00.000Z","updated_at":"2026-04-02T10:05:00.000Z"}},"WebhookEndpointRedeliverResponse":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/WebhookDelivery"}},"required":["data"]},"WebhookDelivery":{"type":"object","properties":{"id":{"type":"string","format":"uuid","example":"550e8400-e29b-41d4-a716-446655440000"},"object":{"type":"string","enum":["webhook_delivery"],"example":"webhook_delivery"},"endpoint_id":{"type":"string","format":"uuid","example":"7c1cbf21-0b6c-4c27-9d60-8a9f0a7e6d91"},"event_id":{"type":"string","format":"uuid","description":"The event this delivery attempted to send.","example":"1f8d9e0b-3a2e-4d9a-b0fe-1c2a3f4d5e6f"},"event_type":{"type":["string","null"],"example":"invoice.paid"},"status":{"type":"string","enum":["pending","succeeded","failed","dead_lettered"],"description":"Delivery state. pending: queued or retrying. succeeded: 2xx response received. failed: last attempt failed, may retry based on attempts/max_attempts. dead_lettered: max attempts exhausted, will not retry automatically."},"attempts":{"type":"integer","example":2},"max_attempts":{"type":"integer","example":8},"last_status_code":{"type":["integer","null"],"description":"HTTP status code returned on the last attempt.","example":502},"last_error":{"type":["string","null"],"description":"Truncated error message from the last failed attempt.","example":"connect ETIMEDOUT"},"last_error_code":{"type":["string","null"],"example":"ETIMEDOUT"},"last_duration_ms":{"type":["integer","null"],"example":1532},"last_attempt_at":{"type":["string","null"],"format":"date-time"},"next_retry_at":{"type":["string","null"],"format":"date-time"},"delivered_at":{"type":["string","null"],"format":"date-time"},"dead_lettered_at":{"type":["string","null"],"format":"date-time"},"dead_letter_reason":{"type":["string","null"]},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"}},"required":["id","object","endpoint_id","event_id","event_type","status","attempts","max_attempts","last_status_code","last_error","last_error_code","last_duration_ms","last_attempt_at","next_retry_at","delivered_at","dead_lettered_at","dead_letter_reason","created_at","updated_at"],"description":"A record of a single webhook delivery attempt chain to one endpoint for one event. Use these records to debug specific delivery failures and to retry individual deliveries."},"WebhookDeliveryListResponse":{"type":"object","properties":{"object":{"type":"string","enum":["list"]},"data":{"type":"array","items":{"$ref":"#/components/schemas/WebhookDelivery"}},"has_more":{"type":"boolean"},"url":{"type":"string"}},"required":["object","data","has_more","url"]},"WebhookDeliveryErrorResponse":{"type":"object","properties":{"error":{"type":"object","properties":{"type":{"type":"string"},"code":{"type":"string"},"message":{"type":"string"}},"required":["type","code","message"]}},"required":["error"]},"WebhookDeliveryResponse":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/WebhookDelivery"}},"required":["data"]},"WebhookDeliveryRetryResponse":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/WebhookDelivery"}},"required":["data"]},"CustomerPortalSessionCreateResponse":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/CustomerPortalSession"}},"required":["data"]},"CustomerPortalSession":{"type":"object","properties":{"id":{"type":"string","description":"Server-generated session identifier. Embedded in the token's `jti` claim.","example":"cpses_kw2e9b0zqrl1x8mv"},"token":{"type":"string","description":"Short-lived bearer token. The customer (or client app acting on their behalf) sends this as `Authorization: Bearer <token>` on every subsequent /v2/customer-portal/* call. Treat as a credential — never log it and never persist it in a browser-accessible store.","example":"rkcps_eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.xxx.yyy"},"customer_id":{"type":"string","format":"uuid","example":"c2a1e7d4-5b3f-4a8e-9d21-6f0b3e4c7a11"},"expires_at":{"type":"string","format":"date-time","description":"ISO-8601 timestamp when this token expires. Default TTL is 60 minutes.","example":"2026-04-18T13:00:00.000Z"},"created_at":{"type":"string","format":"date-time"}},"required":["id","token","customer_id","expires_at","created_at"],"description":"A customer-portal session pair: the opaque bearer token that authenticates the customer, plus metadata about expiry and identity.","example":{"id":"cpses_kw2e9b0zqrl1x8mv","token":"rkcps_eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.xxx.yyy","customer_id":"c2a1e7d4-5b3f-4a8e-9d21-6f0b3e4c7a11","expires_at":"2026-04-18T13:00:00.000Z","created_at":"2026-04-18T12:00:00.000Z"}},"CustomerPortalErrorResponse":{"type":"object","properties":{"error":{"type":"object","properties":{"type":{"type":"string"},"code":{"type":"string"},"message":{"type":"string"}},"required":["type","code","message"]}},"required":["error"]},"CreateCustomerPortalSessionRequest":{"type":"object","properties":{"customer_id":{"type":"string","format":"uuid","description":"UUID of the customer this session will authenticate as.","example":"c2a1e7d4-5b3f-4a8e-9d21-6f0b3e4c7a11"},"ttl_minutes":{"type":"integer","minimum":5,"maximum":240,"description":"Lifetime of the session token in minutes. Between 5 and 240; default 60. Shorter TTLs reduce blast radius if a token leaks.","example":60}},"required":["customer_id"],"description":"Parameters for creating a customer-portal session. The authenticated merchant must already own the referenced customer."},"PortalCustomerResponse":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/PortalCustomer"}},"required":["data"]},"PortalCustomer":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"object":{"type":"string","enum":["customer"]},"email":{"type":["string","null"],"format":"email"},"name":{"type":["string","null"]},"phone":{"type":["string","null"]},"company_name":{"type":["string","null"]},"default_currency":{"type":["string","null"]},"locale":{"type":["string","null"]},"created_at":{"type":"string","format":"date-time"}},"required":["id","object","email","name","phone","company_name","default_currency","locale","created_at"],"description":"The authenticated customer, as visible through the customer-portal surface. This is a deliberately thinner projection than the merchant-facing `Customer` — it omits tax IDs, internal metadata, and anything that could leak merchant-side context to the customer.","example":{"id":"c2a1e7d4-5b3f-4a8e-9d21-6f0b3e4c7a11","object":"customer","email":"ada.lovelace@example.com","name":"Ada Lovelace","phone":"+44 20 7946 0958","company_name":"Analytical Engines Ltd","default_currency":"GBP","locale":"en-GB","created_at":"2026-04-02T10:00:00.000Z"}},"PortalSubscriptionList":{"type":"object","properties":{"object":{"type":"string","enum":["list"]},"data":{"type":"array","items":{"$ref":"#/components/schemas/PortalSubscription"}},"has_more":{"type":"boolean"},"url":{"type":"string"}},"required":["object","data","has_more","url"],"example":{"object":"list","data":[{"id":"d4e6f8a1-2b3c-4d5e-9f60-7a8b9c0d1e24","object":"subscription","status":"active","currency":"GBP","amount_minor":4900,"interval":"month","interval_count":1,"billing_end_strategy":"indefinite","billing_max_cycles":null,"current_period_start":"2026-04-16T10:00:00.000Z","current_period_end":"2026-05-16T10:00:00.000Z","trial_end":"2026-04-16T10:00:00.000Z","cancel_at":null,"canceled_at":null,"started_at":"2026-04-02T10:00:00.000Z","created_at":"2026-04-02T10:00:00.000Z"}],"has_more":false,"url":"/v2/customer-portal/subscriptions"}},"PortalSubscription":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"object":{"type":"string","enum":["subscription"]},"status":{"type":"string"},"currency":{"type":["string","null"]},"amount_minor":{"type":["integer","null"]},"interval":{"type":["string","null"]},"interval_count":{"type":["integer","null"]},"billing_end_strategy":{"type":["string","null"]},"billing_max_cycles":{"type":["integer","null"]},"current_period_start":{"type":["string","null"],"format":"date-time"},"current_period_end":{"type":["string","null"],"format":"date-time"},"trial_end":{"type":["string","null"],"format":"date-time"},"cancel_at":{"type":["string","null"],"format":"date-time"},"canceled_at":{"type":["string","null"],"format":"date-time"},"started_at":{"type":["string","null"],"format":"date-time"},"created_at":{"type":"string","format":"date-time"}},"required":["id","object","status","currency","amount_minor","interval","interval_count","billing_end_strategy","billing_max_cycles","current_period_start","current_period_end","trial_end","cancel_at","canceled_at","started_at","created_at"],"description":"A customer-facing subscription projection. Pricing and cadence fields are exposed; merchant-only bookkeeping fields (plan_id, external IDs) are not.","example":{"id":"d4e6f8a1-2b3c-4d5e-9f60-7a8b9c0d1e24","object":"subscription","status":"active","currency":"GBP","amount_minor":4900,"interval":"month","interval_count":1,"billing_end_strategy":"indefinite","billing_max_cycles":null,"current_period_start":"2026-04-16T10:00:00.000Z","current_period_end":"2026-05-16T10:00:00.000Z","trial_end":"2026-04-16T10:00:00.000Z","cancel_at":null,"canceled_at":null,"started_at":"2026-04-02T10:00:00.000Z","created_at":"2026-04-02T10:00:00.000Z"}},"PortalSubscriptionResponse":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/PortalSubscription"}},"required":["data"]},"PortalSubscriptionCancelResponse":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/PortalSubscription"}},"required":["data"]},"CancelSubscriptionRequest":{"type":"object","properties":{"cancel_at_period_end":{"type":"boolean","default":true,"description":"If `true` (default), cancel at the end of the current period. If `false`, cancel immediately."},"reason":{"type":"string","maxLength":500,"description":"Merchant-visible note recording why the customer canceled."}}},"PortalInvoiceList":{"type":"object","properties":{"object":{"type":"string","enum":["list"]},"data":{"type":"array","items":{"$ref":"#/components/schemas/PortalInvoice"}},"has_more":{"type":"boolean"},"url":{"type":"string"}},"required":["object","data","has_more","url"],"example":{"object":"list","data":[{"id":"f1a2b3c4-d5e6-4270-8a9b-0c1d2e3f4a21","object":"invoice","invoice_number":"INV-0042","status":"paid","currency":"GBP","subtotal":4900,"tax_amount":980,"discount_amount":0,"total":5880,"amount_paid":5880,"amount_due":0,"invoice_date":"2026-04-16T10:00:00.000Z","due_date":"2026-04-16T10:00:00.000Z","paid_at":"2026-04-16T10:02:00.000Z","created_at":"2026-04-16T10:00:00.000Z"}],"has_more":false,"url":"/v2/customer-portal/invoices"}},"PortalInvoice":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"object":{"type":"string","enum":["invoice"]},"invoice_number":{"type":["string","null"]},"status":{"type":"string"},"currency":{"type":["string","null"]},"subtotal":{"type":["integer","null"]},"tax_amount":{"type":["integer","null"]},"discount_amount":{"type":["integer","null"]},"total":{"type":"integer"},"amount_paid":{"type":["integer","null"]},"amount_due":{"type":["integer","null"]},"invoice_date":{"type":["string","null"],"format":"date-time"},"due_date":{"type":["string","null"],"format":"date-time"},"paid_at":{"type":["string","null"],"format":"date-time"},"created_at":{"type":"string","format":"date-time"}},"required":["id","object","invoice_number","status","currency","subtotal","tax_amount","discount_amount","total","amount_paid","amount_due","invoice_date","due_date","paid_at","created_at"],"description":"A customer-facing invoice projection. Lists and retrievals are scoped to invoices owned by the authenticated customer.","example":{"id":"f1a2b3c4-d5e6-4270-8a9b-0c1d2e3f4a21","object":"invoice","invoice_number":"INV-0042","status":"paid","currency":"GBP","subtotal":4900,"tax_amount":980,"discount_amount":0,"total":5880,"amount_paid":5880,"amount_due":0,"invoice_date":"2026-04-16T10:00:00.000Z","due_date":"2026-04-16T10:00:00.000Z","paid_at":"2026-04-16T10:02:00.000Z","created_at":"2026-04-16T10:00:00.000Z"}},"PortalInvoiceResponse":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/PortalInvoice"}},"required":["data"]},"PortalMandateList":{"type":"object","properties":{"object":{"type":"string","enum":["list"]},"data":{"type":"array","items":{"$ref":"#/components/schemas/PortalMandate"}},"has_more":{"type":"boolean"},"url":{"type":"string"}},"required":["object","data","has_more","url"],"example":{"object":"list","data":[{"id":"6a7b8c9d-0e1f-4a32-8c3d-4e5f6a7b8c31","object":"mandate","status":"active","account_holder_name":"Ada Lovelace","account_number_last4":"0000","bank_name":"Example Bank","mandate_reference":"RKEXAM01","next_collection_date":"2026-05-18","created_at":"2026-04-02T10:10:00.000Z"}],"has_more":false,"url":"/v2/customer-portal/mandates"}},"PortalMandate":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"object":{"type":"string","enum":["mandate"]},"status":{"type":"string"},"account_holder_name":{"type":["string","null"]},"account_number_last4":{"type":["string","null"]},"bank_name":{"type":["string","null"]},"mandate_reference":{"type":["string","null"]},"next_collection_date":{"type":["string","null"]},"created_at":{"type":["string","null"],"format":"date-time"}},"required":["id","object","status","account_holder_name","account_number_last4","bank_name","mandate_reference","next_collection_date","created_at"],"description":"A customer-facing Direct Debit mandate projection. Bank details are masked — only the last 4 digits of the account number are exposed.","example":{"id":"6a7b8c9d-0e1f-4a32-8c3d-4e5f6a7b8c31","object":"mandate","status":"active","account_holder_name":"Ada Lovelace","account_number_last4":"0000","bank_name":"Example Bank","mandate_reference":"RKEXAM01","next_collection_date":"2026-05-18","created_at":"2026-04-02T10:10:00.000Z"}},"PortalReauthorizeMandateResponse":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/PortalMandate"}},"required":["data"]},"PortalReauthorizeMandateRequest":{"type":"object","properties":{"account_holder_name":{"type":"string","minLength":1,"maxLength":255},"sort_code":{"type":"string","minLength":6,"maxLength":8},"account_number":{"type":"string","pattern":"^\\d{8}$"},"verification_token":{"type":"string","minLength":1},"acceptance":{"type":"object","properties":{"accountHolder":{"type":"boolean","enum":[true]},"soleSignatory":{"type":"boolean","enum":[true]}},"required":["accountHolder","soleSignatory"]}},"required":["account_holder_name","sort_code","account_number","verification_token","acceptance"]},"PortalReauthorizeSendOtpResponse":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"]},"PortalReauthorizeVerifyOtpResponse":{"type":"object","properties":{"verification_token":{"type":"string"}},"required":["verification_token"]},"PortalReauthorizeVerifyOtpRequest":{"type":"object","properties":{"code":{"type":"string","minLength":4,"maxLength":10}},"required":["code"]},"CustomerMeterList":{"type":"object","properties":{"object":{"type":"string","enum":["list"]},"data":{"type":"array","items":{"$ref":"#/components/schemas/CustomerMeter"}},"has_more":{"type":"boolean","enum":[false],"description":"Customer-meter list responses are not paginated — every meter the merchant has is returned for the specified customer."},"url":{"type":"string"}},"required":["object","data","has_more","url"],"example":{"object":"list","data":[{"object":"customer_meter","customer_id":"c2a1e7d4-5b3f-4a8e-9d21-6f0b3e4c7a11","meter_id":"7c8d9e0f-1a2b-4630-8d4e-5f6a7b8c9d01","meter_name":"API calls","meter_event_name":"api_call","aggregation":"count","unit_name":"calls","total_quantity":4820,"event_count":4820,"last_event_at":"2026-04-18T09:15:00.000Z"}],"has_more":false,"url":"/v2/customer-meters"}},"CustomerMeter":{"type":"object","properties":{"object":{"type":"string","enum":["customer_meter"]},"customer_id":{"type":"string","format":"uuid","example":"c2a1e7d4-5b3f-4a8e-9d21-6f0b3e4c7a11"},"meter_id":{"type":"string","format":"uuid","example":"7c8d9e0f-1a2b-4630-8d4e-5f6a7b8c9d01"},"meter_name":{"type":"string"},"meter_event_name":{"type":"string","description":"The `event_name` the merchant's code posts to /v2/usage-events for this meter.","example":"api_call"},"aggregation":{"type":"string","description":"How quantities are aggregated. Typical values: `sum`, `count`, `max`, `unique`.","example":"count"},"unit_name":{"type":["string","null"],"description":"Human-readable unit for display (e.g. `tokens`, `requests`, `GB`). Merchant-configurable.","example":"calls"},"total_quantity":{"type":"number","description":"Aggregate across all usage events for this (customer, meter) pair. The aggregation function applied matches the meter's `aggregation` field.","example":4820},"event_count":{"type":"integer","description":"Number of `usage_events` rows that contributed to the aggregate. Diagnostic — not the aggregate itself.","example":4820},"last_event_at":{"type":["string","null"],"format":"date-time","description":"Timestamp of the most recent usage event. Null if no events have been recorded."}},"required":["object","customer_id","meter_id","meter_name","meter_event_name","aggregation","unit_name","total_quantity","event_count","last_event_at"],"description":"An on-the-fly aggregate of a customer's consumption for a single meter. RevKeen computes this live from `usage_events` — there is no separate `customer_meters` table to keep in sync.","example":{"object":"customer_meter","customer_id":"c2a1e7d4-5b3f-4a8e-9d21-6f0b3e4c7a11","meter_id":"7c8d9e0f-1a2b-4630-8d4e-5f6a7b8c9d01","meter_name":"API calls","meter_event_name":"api_call","aggregation":"count","unit_name":"calls","total_quantity":4820,"event_count":4820,"last_event_at":"2026-04-18T09:15:00.000Z"}},"CustomerMeterErrorResponse":{"type":"object","properties":{"error":{"type":"object","properties":{"type":{"type":"string"},"code":{"type":"string"},"message":{"type":"string"}},"required":["type","code","message"]}},"required":["error"]},"CustomerMeterResponse":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/CustomerMeter"}},"required":["data"]},"PaymentIntent":{"type":"object","properties":{"id":{"type":"string","description":"Public payment intent ID (pi_xxx)"},"object":{"type":"string","enum":["payment_intent"],"description":"Object type"},"amount":{"type":"integer","description":"Amount in cents"},"amount_capturable":{"type":"integer","description":"Amount that can be captured (for manual capture)"},"amount_received":{"type":"integer","description":"Amount actually received"},"currency":{"type":"string","description":"Three-letter ISO currency code"},"customer":{"type":"string","format":"uuid","description":"Customer ID"},"description":{"type":["string","null"],"description":"Description for merchant reference"},"last_payment_error":{"type":["object","null"],"properties":{"code":{"type":"string","description":"Error code"},"message":{"type":"string","description":"Human-readable error message"},"decline_code":{"type":"string","description":"Card decline code if applicable"},"payment_method":{"type":"object","properties":{"id":{"type":"string","description":"Payment method ID"},"type":{"type":"string","description":"Payment method type"}},"required":["id","type"],"description":"Payment method that caused the error"}},"required":["code","message"],"description":"Last error from payment attempt"},"metadata":{"type":"object","additionalProperties":true,"description":"Custom key-value metadata"},"next_action":{"type":["object","null"],"properties":{"type":{"type":"string","enum":["redirect_to_url","use_stripe_sdk","display_bank_transfer_instructions"],"description":"Type of action required"},"redirect_to_url":{"type":"object","properties":{"url":{"type":"string","format":"uri","description":"URL to redirect customer for 3DS authentication"},"return_url":{"type":"string","format":"uri","description":"URL to return to after 3DS"}},"required":["url","return_url"],"description":"Present when type is redirect_to_url"}},"required":["type"],"description":"Action required from customer (3DS, etc.)"},"payment_method":{"type":["string","null"],"format":"uuid","description":"Payment method ID"},"receipt_email":{"type":["string","null"],"format":"email","description":"Email for receipt"},"statement_descriptor":{"type":["string","null"],"description":"Statement descriptor"},"statement_descriptor_suffix":{"type":["string","null"],"description":"Statement descriptor suffix"},"status":{"type":"string","enum":["requires_payment_method","requires_confirmation","requires_action","processing","succeeded","canceled"],"description":"Payment intent status. requires_payment_method: Needs payment method. requires_confirmation: Ready to confirm. requires_action: Requires customer action (3DS). processing: Being processed. succeeded: Payment complete. canceled: Canceled."},"capture_method":{"type":"string","enum":["automatic","manual"],"description":"Capture method. automatic: Capture immediately on confirmation. manual: Authorize then capture separately."},"client_secret":{"type":"string","description":"Client secret for frontend confirmation"},"canceled_at":{"type":["string","null"],"format":"date-time","description":"When the intent was canceled"},"cancellation_reason":{"type":["string","null"],"enum":["duplicate","fraudulent","requested_by_customer","abandoned","failed_invoice",null],"description":"Why the intent was canceled"},"created":{"type":"integer","description":"Unix timestamp of creation"},"transaction":{"type":["string","null"],"format":"uuid","description":"The sale transaction this payment intent recorded once it succeeded, or null. Pass it as `payment_id` to `POST /v2/refunds` to refund the payment."},"livemode":{"type":"boolean","description":"Whether in live mode"}},"required":["id","object","amount","amount_capturable","amount_received","currency","customer","status","capture_method","client_secret","created","livemode"],"description":"A PaymentIntent tracks the lifecycle of a payment from creation through authorization, optional 3DS authentication, and final capture.","example":{"id":"pi_k3m9x2q7w8e1r4t6","object":"payment_intent","amount":5880,"amount_capturable":0,"amount_received":5880,"currency":"gbp","customer":"c2a1e7d4-5b3f-4a8e-9d21-6f0b3e4c7a11","description":"INV-0042","last_payment_error":null,"metadata":{},"next_action":null,"payment_method":"9e8d7c6b-5a4f-4e3d-8c2b-1a0f9e8d7c65","receipt_email":"ada.lovelace@example.com","statement_descriptor":"REVKEEN PRO","statement_descriptor_suffix":null,"status":"succeeded","capture_method":"automatic","client_secret":"pi_k3m9x2q7w8e1r4t6_secret_example","canceled_at":null,"cancellation_reason":null,"created":1776333720,"transaction":"7b8c9d0e-1f2a-4b43-9d4e-5f6a7b8c9d42","livemode":true}},"PaymentIntentErrorResponse":{"type":"object","properties":{"error":{"type":"object","properties":{"type":{"type":"string","description":"Error type (api_error, invalid_request_error, etc.)"},"code":{"type":"string","description":"Error code"},"message":{"type":"string","description":"Human-readable error message"},"param":{"type":"string","description":"Parameter that caused the error"}},"required":["type","code","message"]}},"required":["error"],"description":"Error response returned when a payment intent operation fails."},"CreatePaymentIntentRequest":{"type":"object","properties":{"amount":{"type":"integer","minimum":1,"description":"Amount in cents. Must be greater than 0.","example":5000},"currency":{"type":"string","minLength":3,"maxLength":3,"description":"Three-letter ISO currency code. Defaults to the merchant's default currency.","example":"USD"},"customer":{"type":"string","format":"uuid","description":"Customer ID to charge"},"payment_method":{"type":"string","format":"uuid","description":"Payment method ID. If not provided, status will be requires_payment_method."},"capture_method":{"type":"string","enum":["automatic","manual"],"default":"automatic","description":"Capture method. Defaults to automatic. Only `automatic` is accepted for now: `manual` is refused with 400 `capture_method_unsupported`, because a payment intent is charged in full when it is confirmed."},"description":{"type":"string","maxLength":500,"description":"Description for merchant reference"},"statement_descriptor":{"type":"string","maxLength":22,"description":"Statement descriptor shown on customer's statement"},"statement_descriptor_suffix":{"type":"string","maxLength":22,"description":"Statement descriptor suffix"},"receipt_email":{"type":"string","format":"email","description":"Email to send receipt to"},"metadata":{"type":"object","additionalProperties":true,"description":"Custom key-value metadata"},"gateway_merchant_id":{"type":"string","description":"Specific gateway merchant ID for multi-MID setups (NMI)"}},"required":["amount","customer"],"description":"Parameters for creating a new payment intent. Specify the amount, currency, customer, and optionally a payment method and capture strategy."},"PaymentIntentListResponse":{"type":"object","properties":{"object":{"type":"string","enum":["list"]},"data":{"type":"array","items":{"$ref":"#/components/schemas/PaymentIntent"}},"has_more":{"type":"boolean"},"url":{"type":"string","description":"The URL for accessing this list"}},"required":["object","data","has_more","url"],"description":"A paginated list of payment intents matching the query filters.","example":{"object":"list","data":[{"id":"pi_k3m9x2q7w8e1r4t6","object":"payment_intent","amount":5880,"amount_capturable":0,"amount_received":5880,"currency":"gbp","customer":"c2a1e7d4-5b3f-4a8e-9d21-6f0b3e4c7a11","description":"INV-0042","last_payment_error":null,"metadata":{},"next_action":null,"payment_method":"9e8d7c6b-5a4f-4e3d-8c2b-1a0f9e8d7c65","receipt_email":"ada.lovelace@example.com","statement_descriptor":"REVKEEN PRO","statement_descriptor_suffix":null,"status":"succeeded","capture_method":"automatic","client_secret":"pi_k3m9x2q7w8e1r4t6_secret_example","canceled_at":null,"cancellation_reason":null,"created":1776333720,"transaction":"7b8c9d0e-1f2a-4b43-9d4e-5f6a7b8c9d42","livemode":true}],"has_more":false,"url":"/v2/payment-intents"}},"PaymentIntentListValidationError":{"type":"object","properties":{"error":{"type":"object","properties":{"type":{"type":"string","enum":["invalid_request_error"]},"code":{"type":"string","enum":["parameter_invalid"]},"message":{"type":"string"},"request_id":{"type":"string"},"param":{"type":"string","enum":["customer","status","created_gte","created_lte","limit","starting_after","ending_before"],"description":"The query parameter that was rejected."}},"required":["type","code","message","param"]}},"required":["error"],"description":"Returned when a query parameter fails validation. `param` names the parameter to fix."},"PaymentIntentRetrieveValidationError":{"type":"object","properties":{"error":{"type":"object","properties":{"type":{"type":"string","enum":["invalid_request_error"]},"code":{"type":"string","enum":["parameter_invalid"]},"message":{"type":"string"},"request_id":{"type":"string"},"param":{"type":"string","enum":["id"],"description":"The query parameter that was rejected."}},"required":["type","code","message","param"]}},"required":["error"],"description":"Returned when a query parameter fails validation. `param` names the parameter to fix."},"UpdatePaymentIntentRequest":{"type":"object","properties":{"amount":{"type":"integer","minimum":1,"description":"Amount in cents"},"currency":{"type":"string","minLength":3,"maxLength":3,"description":"Three-letter ISO currency code"},"payment_method":{"type":"string","format":"uuid","description":"Payment method ID"},"description":{"type":"string","maxLength":500,"description":"Description for merchant reference"},"statement_descriptor":{"type":"string","maxLength":22,"description":"Statement descriptor"},"statement_descriptor_suffix":{"type":"string","maxLength":22,"description":"Statement descriptor suffix"},"receipt_email":{"type":"string","format":"email","description":"Email to send receipt to"},"metadata":{"type":"object","additionalProperties":true,"description":"Custom key-value metadata"}},"description":"Parameters for updating a payment intent before confirmation. Only amount, description, payment method, and metadata can be changed."},"ConfirmPaymentIntentRequest":{"type":"object","properties":{"payment_method":{"type":"string","format":"uuid","description":"Payment method ID. Required if not already set on the intent."},"return_url":{"type":"string","format":"uri","description":"URL to redirect to after 3DS authentication"}},"description":"Parameters for confirming a payment intent. Optionally attach a payment method and specify a return URL for 3DS flows."},"CapturePaymentIntentRequest":{"type":"object","properties":{"amount_to_capture":{"type":"integer","minimum":1,"description":"Amount to capture in cents. Defaults to full authorized amount."}},"description":"Parameters for capturing a previously authorized payment intent. Specify amount_to_capture for partial captures."},"CancelPaymentIntentRequest":{"type":"object","properties":{"cancellation_reason":{"type":"string","enum":["duplicate","fraudulent","requested_by_customer","abandoned","failed_invoice"],"description":"Reason for cancellation"}},"description":"Parameters for canceling a payment intent that has not yet been captured."},"SetupIntent":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Unique identifier for the setup intent","example":"550e8400-e29b-41d4-a716-446655440000"},"object":{"type":"string","enum":["setup_intent"],"description":"Object type, always 'setup_intent'"},"public_id":{"type":"string","description":"Public ID visible in API responses (seti_xxx format)","example":"seti_1a2b3c4d5e6f"},"status":{"type":"string","enum":["requires_payment_method","requires_confirmation","requires_action","processing","succeeded","canceled"],"description":"The status of the setup intent","example":"requires_payment_method"},"customer_id":{"type":["string","null"],"format":"uuid","description":"ID of the customer this setup intent is for"},"payment_method_id":{"type":["string","null"],"format":"uuid","description":"ID of the payment method being set up"},"payment_method_types":{"type":"array","items":{"type":"string"},"description":"Allowed payment method types for this setup","example":["card"]},"usage":{"type":"string","enum":["on_session","off_session"],"description":"Indicates how the payment method will be used","example":"off_session"},"next_action":{"$ref":"#/components/schemas/SetupIntentNextAction"},"client_secret":{"type":"string","description":"Client secret for frontend confirmation","example":"seti_1a2b3c4d5e6f_secret_xyz789"},"gateway":{"type":"string","description":"Name of the payment processor that handled this setup intent"},"last_error":{"$ref":"#/components/schemas/SetupIntentError"},"cancellation_reason":{"type":["string","null"],"enum":["abandoned","requested_by_customer","duplicate",null],"description":"Reason for cancellation if canceled","example":"requested_by_customer"},"description":{"type":["string","null"],"description":"Merchant description for reference"},"metadata":{"type":"object","additionalProperties":{},"description":"Custom metadata attached to the setup intent"},"confirmed_at":{"type":["string","null"],"format":"date-time","description":"When the setup intent was confirmed"},"canceled_at":{"type":["string","null"],"format":"date-time","description":"When the setup intent was canceled"},"created_at":{"type":"string","format":"date-time","description":"When the setup intent was created"},"updated_at":{"type":"string","format":"date-time","description":"When the setup intent was last updated"}},"required":["id","object","public_id","status","customer_id","payment_method_id","payment_method_types","usage","next_action","client_secret","gateway","last_error","cancellation_reason","description","metadata","confirmed_at","canceled_at","created_at","updated_at"],"description":"A SetupIntent saves a payment method for future use WITHOUT charging the customer. Use it to collect and verify card details before storing them."},"SetupIntentNextAction":{"type":["object","null"],"properties":{"type":{"type":"string","enum":["redirect_to_url","use_stripe_sdk"],"description":"The type of action to take"},"redirect_to_url":{"type":"object","properties":{"url":{"type":"string","format":"uri","description":"The URL to redirect the customer to"},"return_url":{"type":"string","format":"uri","description":"The URL to redirect back to after completion"}},"required":["url","return_url"],"description":"Redirect details for 3DS authentication"},"use_stripe_sdk":{"type":"object","additionalProperties":{},"description":"SDK-specific data for client-side handling"}},"required":["type"],"description":"Action required from customer (3DS redirect, etc.)"},"SetupIntentError":{"type":["object","null"],"properties":{"code":{"type":"string","description":"Error code","example":"authentication_failed"},"message":{"type":"string","description":"Human-readable error message","example":"3DS authentication failed"},"decline_code":{"type":"string","description":"Decline code from the payment gateway","example":"authentication_required"},"payment_method":{"type":"object","properties":{"id":{"type":"string"},"type":{"type":"string"}},"required":["id","type"],"description":"The payment method that caused the error"}},"required":["code","message"],"description":"Last error encountered during setup"},"CreateSetupIntentRequest":{"type":"object","properties":{"customer_id":{"type":"string","format":"uuid","description":"ID of the customer to attach the payment method to","example":"550e8400-e29b-41d4-a716-446655440000"},"payment_method_types":{"type":"array","items":{"type":"string"},"default":["card"],"description":"Allowed payment method types","example":["card"]},"usage":{"type":"string","enum":["on_session","off_session"],"default":"off_session","description":"How the payment method will be used","example":"off_session"},"description":{"type":"string","description":"Merchant description for reference","example":"Saving card for subscription payments"},"metadata":{"type":"object","additionalProperties":{},"description":"Custom metadata to attach","example":{"source":"checkout"}}},"description":"Parameters for creating a setup intent to save a payment method for a customer."},"SetupIntentList":{"type":"object","properties":{"object":{"type":"string","enum":["list"],"description":"Object type, always 'list'"},"data":{"type":"array","items":{"$ref":"#/components/schemas/SetupIntent"},"description":"Array of setup intents"},"has_more":{"type":"boolean","description":"Whether there are more results available"},"total_count":{"type":"integer","description":"Total count of matching setup intents"},"url":{"type":"string","description":"URL for this list resource","example":"/v2/setup-intents"}},"required":["object","data","has_more","total_count","url"],"description":"A paginated list of setup intents matching the query filters."},"UpdateSetupIntentRequest":{"type":"object","properties":{"customer_id":{"type":"string","format":"uuid","description":"ID of the customer to attach the payment method to"},"payment_method_types":{"type":"array","items":{"type":"string"},"description":"Allowed payment method types"},"description":{"type":"string","description":"Merchant description for reference"},"metadata":{"type":"object","additionalProperties":{},"description":"Custom metadata to attach"}},"description":"Parameters for updating a setup intent before confirmation."},"ConfirmSetupIntentRequest":{"type":"object","properties":{"payment_method_id":{"type":"string","format":"uuid","description":"ID of the payment method to confirm with","example":"550e8400-e29b-41d4-a716-446655440000"},"return_url":{"type":"string","format":"uri","description":"URL to redirect to after 3DS authentication","example":"https://example.com/setup/complete"}},"description":"Parameters for confirming a setup intent, which verifies the payment method and saves it for future use."},"CancelSetupIntentRequest":{"type":"object","properties":{"cancellation_reason":{"type":"string","enum":["abandoned","requested_by_customer","duplicate"],"description":"Reason for cancellation","example":"requested_by_customer"}},"description":"Parameters for canceling a setup intent that is no longer needed."},"PaymentMethodList":{"type":"object","properties":{"object":{"type":"string","enum":["list"],"description":"Object type, always 'list'"},"data":{"type":"array","items":{"$ref":"#/components/schemas/PaymentMethod"},"description":"Array of payment methods"},"has_more":{"type":"boolean","description":"Whether there are more results available"},"url":{"type":"string","description":"URL for this list resource","example":"/v2/payment-methods"}},"required":["object","data","has_more","url"],"description":"A list of payment methods attached to a customer, with optional type filtering."},"UpdatePaymentMethodRequest":{"type":"object","properties":{"billing_details":{"allOf":[{"$ref":"#/components/schemas/BillingDetails"},{"description":"Updated billing details"}]},"metadata":{"type":"object","additionalProperties":{},"description":"Custom metadata to attach"}},"description":"Parameters for updating a payment method's billing details or metadata."},"AttachPaymentMethodRequest":{"type":"object","properties":{"customer_id":{"type":"string","format":"uuid","description":"ID of the customer to attach the payment method to","example":"550e8400-e29b-41d4-a716-446655440000"}},"required":["customer_id"],"description":"Parameters for attaching a payment method to a customer. The payment method must have been created first."},"PaymentMethodConflictError":{"type":"object","properties":{"error":{"type":"object","properties":{"type":{"type":"string","enum":["invalid_request_error"]},"code":{"type":"string","enum":["payment_method_in_use","payment_method_not_active"]},"message":{"type":"string"},"param":{"type":"string"},"request_id":{"type":"string"},"details":{"type":"object","properties":{"reason":{"type":"string","enum":["customer_default","active_subscription"],"description":"Why a detach was refused: the method is the customer's default, or an active, trialing or past_due subscription bills on it."},"subscription_ids":{"type":"array","items":{"type":"string","format":"uuid"},"description":"The subscriptions that bill on this method (reason `active_subscription`)."}}}},"required":["type","code","message"]}},"required":["error"],"description":"Returned when the payment method cannot be detached because it is still in use, or cannot become the default because it is not active. Nothing was changed."},"EventListResponse":{"type":"object","properties":{"object":{"type":"string","enum":["list"]},"data":{"type":"array","items":{"$ref":"#/components/schemas/Event"}},"has_more":{"type":"boolean"},"url":{"type":"string","description":"The URL for accessing this list"}},"required":["object","data","has_more","url"],"description":"A paginated list of events matching the query filters."},"Event":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Unique event identifier"},"object":{"type":"string","enum":["event"],"description":"Object type"},"type":{"type":"string","description":"Event type (e.g., invoice.paid, payment.succeeded)","example":"invoice.paid"},"data":{"type":"object","properties":{"object":{"description":"The object that triggered the event (invoice, payment, etc.)"},"previous_attributes":{"description":"Previous values of attributes that changed (for update events)"}},"description":"Event data containing the affected object"},"request":{"$ref":"#/components/schemas/EventRequest"},"pending_webhooks":{"type":"integer","description":"Number of webhooks still pending delivery for this event","example":0},"api_version":{"type":["string","null"],"description":"API version used to render this event","example":"2026-01-15"},"livemode":{"type":"boolean","description":"Whether this event was produced in live mode","example":true},"created":{"type":"integer","description":"Unix timestamp when the event was created","example":1704067200}},"required":["id","object","type","data","request","pending_webhooks","api_version","livemode","created"],"description":"An event represents something that happened in your account — a payment succeeded, an invoice was created, a subscription changed. Events are the source of truth for webhook delivery."},"EventRequest":{"type":["object","null"],"properties":{"id":{"type":["string","null"],"description":"Request ID that triggered this event (if applicable)"},"idempotency_key":{"type":["string","null"],"description":"Idempotency key used in the request (if applicable)"}},"required":["id","idempotency_key"],"description":"Information about the request that triggered this event"},"EventErrorResponse":{"type":"object","properties":{"error":{"type":"object","properties":{"type":{"type":"string","description":"Error type"},"code":{"type":"string","description":"Error code"},"message":{"type":"string","description":"Human-readable error message"},"param":{"type":"string","description":"Parameter that caused the error"}},"required":["type","code","message"]}},"required":["error"],"description":"Error response returned when an event operation fails."},"ResendWebhookResponse":{"type":"object","properties":{"success":{"type":"boolean","description":"Whether the webhook was queued for resending"},"webhook_endpoint_id":{"type":"string","format":"uuid","description":"The webhook endpoint ID the event was sent to"},"message":{"type":"string","description":"Additional information"}},"required":["success"],"description":"Confirmation that a webhook event was resent to the specified endpoint."},"TestEventResponse":{"type":"object","properties":{"success":{"type":"boolean","description":"Whether the test event was created"},"event":{"allOf":[{"$ref":"#/components/schemas/Event"},{"description":"The created test event"}]},"message":{"type":"string","description":"Additional information"}},"required":["success"],"description":"The test event that was sent, including delivery status."},"CreateTestEventRequest":{"type":"object","properties":{"type":{"type":"string","enum":["checkout.session.completed","checkout.session.expired","checkout.session.async_payment_succeeded","checkout.session.async_payment_failed","invoice.created","invoice.finalized","invoice.paid","invoice.payment_failed","invoice.voided","invoice.marked_uncollectible","invoice.upcoming","invoice.sent","payment.succeeded","payment.failed","payment.refunded","payment.disputed","payment_intent.created","payment_intent.succeeded","payment_intent.payment_failed","payment_intent.canceled","payment_intent.requires_action","payment_method.attached","payment_method.detached","payment_method.updated","subscription.created","subscription.updated","subscription.canceled","subscription.paused","subscription.resumed","subscription.plan_changed","subscription.trial_will_end","subscription.renewed","customer.created","customer.updated","customer.deleted","refund.created","refund.updated","refund.failed","dispute.created","dispute.updated","dispute.closed","order.created","order.paid","order.canceled"],"description":"The type of test event to create","example":"invoice.paid"}},"required":["type"],"description":"Parameters for creating a test event to verify your webhook endpoint integration."},"SubscriptionSchedule":{"type":"object","properties":{"id":{"type":"string","description":"Public schedule ID (sub_sched_xxx)"},"object":{"type":"string","enum":["subscription_schedule"],"description":"Object type"},"status":{"type":"string","enum":["not_started","active","completed","canceled","released"],"description":"Schedule status. not_started: Scheduled for future. active: Currently executing phases. completed: All phases finished. canceled: Manually canceled. released: Detached from subscription."},"customer":{"type":["string","null"],"format":"uuid","description":"Customer ID"},"subscription":{"type":["string","null"],"format":"uuid","description":"Linked subscription ID"},"current_phase":{"type":["object","null"],"properties":{"start_date":{"type":"integer","description":"Phase start (Unix timestamp)"},"end_date":{"type":["integer","null"],"description":"Phase end (Unix timestamp)"}},"required":["start_date","end_date"],"description":"Current phase being executed"},"phases":{"type":"array","items":{"$ref":"#/components/schemas/SchedulePhase"},"description":"All phases in the schedule"},"end_behavior":{"type":"string","enum":["cancel","release"],"description":"What happens when the schedule completes. cancel: Cancel the subscription. release: Make subscription standalone."},"released_at":{"type":["integer","null"],"description":"When released (Unix timestamp)"},"released_subscription":{"type":["string","null"],"format":"uuid","description":"Subscription ID at release time"},"metadata":{"type":"object","additionalProperties":true,"description":"Custom metadata"},"created":{"type":"integer","description":"Unix timestamp of creation"},"livemode":{"type":"boolean","description":"Whether in live mode"}},"required":["id","object","status","customer","subscription","current_phase","phases","end_behavior","created","livemode"],"description":"A subscription schedule defines future changes to a subscription. Each phase specifies different pricing, quantities, or plans that take effect at specified dates."},"SchedulePhase":{"type":"object","properties":{"items":{"type":"array","items":{"$ref":"#/components/schemas/PhaseItem"},"minItems":1,"description":"Products/prices included in this phase"},"start_date":{"type":"string","description":"When this phase starts (ISO 8601 date)","example":"2026-03-01T00:00:00Z"},"end_date":{"type":["string","null"],"description":"When this phase ends (null for indefinite)","example":"2026-06-01T00:00:00Z"},"proration_behavior":{"type":"string","enum":["create_prorations","none","always_invoice"],"description":"How to handle prorations when entering this phase"},"billing_cycle_anchor":{"type":["string","null"],"description":"Override billing cycle anchor for this phase"},"default_payment_method":{"type":["string","null"],"format":"uuid","description":"Payment method to use for this phase"},"collection_method":{"type":"string","enum":["charge_automatically","send_invoice"],"description":"How to collect payment for this phase"},"coupon":{"type":["string","null"],"description":"Coupon code to apply during this phase"},"trial_end":{"type":["string","null"],"description":"End of trial period (if applicable)"},"metadata":{"type":"object","additionalProperties":true,"description":"Phase-specific metadata"}},"required":["items","start_date"],"description":"A time-bounded phase within a subscription schedule, with its own pricing, quantities, and billing configuration."},"PhaseItem":{"type":"object","properties":{"price_id":{"type":"string","format":"uuid","description":"Price ID for this item"},"quantity":{"type":"integer","minimum":1,"default":1,"description":"Quantity of this item"}},"required":["price_id"],"description":"A product-price pair within a schedule phase, defining what the customer is billed for during that phase."},"SubscriptionScheduleErrorResponse":{"type":"object","properties":{"error":{"type":"object","properties":{"type":{"type":"string","description":"Error type"},"code":{"type":"string","description":"Error code"},"message":{"type":"string","description":"Human-readable error message"},"param":{"type":"string","description":"Parameter that caused the error"}},"required":["type","code","message"]}},"required":["error"],"description":"Error response returned when a subscription schedule operation fails."},"CreateSubscriptionScheduleRequest":{"type":"object","properties":{"customer":{"type":"string","format":"uuid","description":"Customer ID (required if no subscription)"},"subscription":{"type":"string","format":"uuid","description":"Existing subscription to attach schedule to"},"start_date":{"anyOf":[{"type":"string","enum":["now"]},{"type":"string","format":"date-time"},{"type":"integer"}],"description":"When schedule starts. 'now', ISO date, or Unix timestamp. Defaults to now.","example":"now"},"phases":{"type":"array","items":{"$ref":"#/components/schemas/SchedulePhase"},"minItems":1,"description":"Phases defining the schedule (at least one required)"},"end_behavior":{"type":"string","enum":["cancel","release"],"default":"cancel","description":"What happens when all phases complete"},"metadata":{"type":"object","additionalProperties":true,"description":"Custom metadata"}},"required":["phases"],"description":"Parameters for creating a subscription schedule with one or more phases of pricing and billing changes."},"SubscriptionScheduleListResponse":{"type":"object","properties":{"object":{"type":"string","enum":["list"]},"data":{"type":"array","items":{"$ref":"#/components/schemas/SubscriptionSchedule"}},"has_more":{"type":"boolean"},"url":{"type":"string","description":"The URL for accessing this list"}},"required":["object","data","has_more","url"],"description":"A paginated list of subscription schedules matching the query filters."},"UpdateSubscriptionScheduleRequest":{"type":"object","properties":{"phases":{"type":"array","items":{"$ref":"#/components/schemas/SchedulePhase"},"minItems":1,"description":"Replace all phases"},"end_behavior":{"type":"string","enum":["cancel","release"],"description":"What happens when all phases complete"},"proration_behavior":{"type":"string","enum":["create_prorations","none","always_invoice"],"description":"Proration behavior when updating phases"},"metadata":{"type":"object","additionalProperties":true,"description":"Custom metadata"}},"description":"Parameters for updating a subscription schedule's phases before they take effect."},"CancelSubscriptionScheduleRequest":{"type":"object","properties":{"invoice_now":{"type":"boolean","description":"Generate a final invoice immediately"},"prorate":{"type":"boolean","description":"Prorate final invoice"}},"description":"Parameters for canceling a subscription schedule, optionally reverting the subscription to its pre-schedule state."},"ReleaseSubscriptionScheduleRequest":{"type":"object","properties":{"preserve_cancel_date":{"type":"boolean","description":"Keep the subscription's cancel_at date if set"}},"description":"Parameters for releasing a subscription schedule, detaching it from the subscription while keeping current terms."},"TerminalPaymentResponse":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/TerminalPayment"}},"required":["data"],"description":"A single terminal payment object with full device and transaction details."},"TerminalPayment":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Unique identifier for this terminal payment attempt"},"invoice_id":{"type":["string","null"],"format":"uuid","description":"Associated invoice ID, or null for walk-in/ad-hoc payments"},"device_id":{"type":["string","null"],"format":"uuid","description":"The terminal device that processed (or is processing) this payment"},"type":{"type":"string","enum":["sale","refund","void"],"description":"Transaction type. sale: original charge. refund: money returned. void: pre-settlement cancellation."},"status":{"type":"string","enum":["requested","in_progress","approved","declined","cancelled","error","timed_out","standalone_approved","standalone_declined"],"description":"Payment lifecycle status. requested: command sent, awaiting card. in_progress: terminal processing. approved: payment succeeded. declined: issuer declined. cancelled: merchant cancelled. error: terminal error. timed_out: no response within 3 minutes."},"amount_minor":{"type":"integer","description":"Amount in minor units (e.g., pence for GBP, cents for USD)"},"currency":{"type":"string","description":"ISO 4217 currency code"},"reference":{"type":["string","null"],"description":"Payment reference (invoice number or custom reference)"},"terminal_serial":{"type":["string","null"],"description":"Serial number of the PAX terminal that processed this payment"},"uti":{"type":["string","null"],"description":"Unique Transaction Identifier from the terminal"},"auth_code":{"type":["string","null"],"description":"Authorization code from the payment processor. Present when approved."},"response_code":{"type":["string","null"],"description":"Terminal response code. '00' indicates approval."},"rrn":{"type":["string","null"],"description":"Retrieval Reference Number for settlement reconciliation"},"card_scheme":{"type":["string","null"],"description":"Card network (e.g., VISA, MASTERCARD, AMEX)"},"masked_pan":{"type":["string","null"],"description":"Masked card number — only the last 4 digits are visible"},"entry_mode":{"type":["string","null"],"description":"How the card was read: contactless (NFC tap), emv_chip (chip insert), magnetic_stripe (swipe), manual_entry (keyed), or fallback (chip-to-swipe)"},"error_message":{"type":["string","null"],"description":"Human-readable error message when status is error or timed_out"},"created_at":{"type":"string","format":"date-time","description":"ISO 8601 timestamp when the payment was initiated"},"completed_at":{"type":["string","null"],"format":"date-time","description":"ISO 8601 timestamp when the terminal returned a result, or null if still in progress"}},"required":["id","invoice_id","device_id","type","status","amount_minor","currency","reference","terminal_serial","uti","auth_code","response_code","rrn","card_scheme","masked_pan","entry_mode","error_message","created_at","completed_at"],"description":"A card-present payment processed through a POS terminal, with device details and card entry mode."},"TerminalPaymentErrorResponse":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"},"code":{"type":"string"}},"required":["error"],"description":"Error response returned when a terminal payment operation fails."},"CreateTerminalPaymentRequest":{"type":"object","properties":{"device_id":{"type":"string","format":"uuid","description":"The ID of the terminal device to send the payment to. Use List Devices to discover available device IDs. Even merchants with a single terminal must pass the device_id explicitly — there is no auto-routing fallback.","example":"d1e2f3a4-b5c6-7890-abcd-ef1234567890"},"amount_minor":{"type":"integer","exclusiveMinimum":0,"description":"Amount in minor units (e.g., pence for GBP, cents for USD)","example":5000},"currency":{"type":"string","minLength":3,"maxLength":3,"description":"ISO 4217 currency code","example":"GBP"},"invoice_id":{"type":"string","format":"uuid","description":"The invoice to charge. Omit for walk-in or ad-hoc payments where no invoice exists. When omitted, the terminal payment is recorded without an invoice association."},"reference":{"type":"string","maxLength":200,"description":"Custom reference for the payment. Auto-generated if not provided.","example":"walk-in-sale-001"}},"required":["device_id","amount_minor","currency"],"description":"Parameters for initiating a card-present payment on a POS terminal device."},"TerminalPaymentListResponse":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/TerminalPayment"}},"meta":{"type":"object","properties":{"count":{"type":"number"},"limit":{"type":"number"}},"required":["count","limit"]}},"required":["data","meta"],"description":"A paginated list of terminal payments matching the query filters."},"RefundTerminalPaymentRequest":{"type":"object","properties":{"amount_minor":{"type":"integer","exclusiveMinimum":0,"description":"Amount to refund in minor units. Omit for a full refund.","example":2500},"reason":{"type":"string","maxLength":500,"description":"Reason for the refund","example":"Customer return"},"restock_line_items":{"type":"array","items":{"type":"object","properties":{"invoice_line_item_id":{"type":"string","format":"uuid","description":"An invoice line of the refunded payment"},"quantity":{"type":"integer","minimum":1,"description":"Units of that line to put back on sale","example":1}},"required":["invoice_line_item_id","quantity"]},"maxItems":100,"description":"Units to put back on sale once the refund is approved, by invoice line. Omit to restock nothing."}},"description":"Parameters for refunding a completed terminal payment, optionally specifying a partial amount."},"VoidTerminalPaymentRequest":{"type":"object","properties":{"reason":{"type":"string","maxLength":500,"description":"Reason for the void","example":"Duplicate charge"}},"description":"Parameters for voiding an unsettled terminal payment before end-of-day settlement."},"TerminalDeviceListResponse":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/TerminalDevice"}},"meta":{"type":"object","properties":{"count":{"type":"number"}},"required":["count"]}},"required":["data","meta"],"description":"A list of terminal devices registered for your merchant account."},"TerminalDevice":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Unique identifier for this terminal device. Use this as the device_id when initiating payments."},"device_name":{"type":["string","null"],"description":"Friendly name assigned to this device (e.g., 'Front Desk Terminal')"},"terminal_serial":{"type":["string","null"],"description":"Serial number of the paired PAX terminal hardware"},"terminal_ip":{"type":["string","null"],"description":"The LAN IP address of the terminal as seen by the connector on the merchant's local network. This is not a publicly reachable address — all commands flow through the RevKeen cloud."},"platform":{"type":["string","null"],"enum":["macos","windows","linux",null],"description":"Operating system of the machine running the Terminal Connector application"},"status":{"type":"string","enum":["online","offline","pairing"],"description":"Device connectivity status. online: connector connected and heartbeat within 5 minutes. offline: heartbeat stale or connector disconnected. pairing: connector registered but terminal not yet paired."},"terminal_paired":{"type":"boolean","description":"Whether a PAX terminal has been paired to this connector device. Must be true to accept payments."},"terminal_reachable":{"type":["boolean","null"],"description":"Whether the connector can currently reach the PAX terminal on the local network"},"app_version":{"type":["string","null"],"description":"Version of the Terminal Connector application"},"last_heartbeat_at":{"type":["string","null"],"format":"date-time","description":"ISO 8601 timestamp of the last heartbeat received from this device. Devices with no heartbeat in 5 minutes are considered offline."}},"required":["id","device_name","terminal_serial","terminal_ip","platform","status","terminal_paired","terminal_reachable","app_version","last_heartbeat_at"],"description":"A POS terminal device registered to your merchant account, with connection status and capabilities."},"TerminalDeviceErrorResponse":{"type":"object","properties":{"error":{"type":"string"},"message":{"type":"string"}},"required":["error"],"description":"Error response returned when a terminal device operation fails."},"TerminalDeviceResponse":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/TerminalDevice"}},"required":["data"],"description":"A single terminal device with connection status and configuration details."},"BatchIngestResult":{"type":"object","properties":{"object":{"type":"string","enum":["usage_event_batch_result"]},"summary":{"type":"object","properties":{"ingested":{"type":"number"},"duplicate":{"type":"number"},"skipped":{"type":"number"},"failed":{"type":"number"}},"required":["ingested","duplicate","skipped","failed"]},"data":{"type":"array","items":{"type":"object","properties":{"index":{"type":"number"},"status":{"type":"string","enum":["ingested","duplicate","skipped","failed"]},"id":{"type":"string"},"reason":{"type":"string"}},"required":["index","status"]}}},"required":["object","summary","data"],"description":"Result of a batch ingest operation showing how many events were accepted, skipped, duplicated, or rejected. duplicate means the event's idempotency_key has already been seen for this merchant.","example":{"object":"usage_event_batch_result","summary":{"ingested":1,"duplicate":0,"skipped":0,"failed":0},"data":[{"index":0,"status":"ingested","id":"8d9e0f1a-2b3c-4641-9e5f-6a7b8c9d0e12"}]}},"UsageEventError":{"type":"object","properties":{"error":{"anyOf":[{"type":"string"},{"type":"object","properties":{"message":{"type":"string"},"type":{"type":"string"}},"required":["message","type"]}]}},"required":["error"],"description":"Error response returned when a usage event operation fails."},"UsageIngestRequest":{"anyOf":[{"$ref":"#/components/schemas/UsageEvent"},{"$ref":"#/components/schemas/BatchIngestRequest"}],"description":"Usage event ingest request. Send either one usage event object directly, or wrap up to 1000 events in an events array."},"UsageEvent":{"type":"object","properties":{"name":{"type":"string","description":"Event name matching a meter's event_name","example":"api_call"},"customer_id":{"type":"string","description":"RevKeen customer ID. Prefer external_customer_id unless your system already stores RevKeen customer UUIDs."},"external_customer_id":{"type":"string","description":"Your external customer identifier. Recommended attribution field; unknown values fail rather than creating orphan usage.","example":"usr_123"},"subscription_id":{"type":"string","description":"Subscription ID to attribute usage to. Usage is assumed to share the subscription currency; RevKeen does not perform FX conversion in metering."},"meter_id":{"type":"string","description":"Direct meter ID (alternative to event name matching)"},"quantity":{"type":"number","default":1,"description":"Event quantity (default: 1). For percentage-priced meters, send the signed transaction amount in the subscription currency's minor units.","example":1},"timestamp":{"type":"string","format":"date-time","description":"ISO 8601 timestamp (default: now)","example":"2026-03-14T10:30:00Z"},"idempotency_key":{"type":"string","minLength":1,"description":"REQUIRED merchant-scoped deduplication key. Reusing the same key returns duplicate status and does not create another billable event. Compose it from stable business identifiers (entity ids, types), never from timestamps.","example":"evt_unique_123"},"metadata":{"type":"object","additionalProperties":{},"description":"Arbitrary properties for filtering and aggregation"}},"required":["name","idempotency_key"],"description":"A single usage event representing customer consumption of a metered resource. Provide one of customer_id, external_customer_id, or subscription_id. Idempotency is permanent per merchant via the usage_events merchant/idempotency unique index. Each subscription has one currency; usage events attributed to it are interpreted in that currency with no metering-layer FX conversion."},"BatchIngestRequest":{"type":"object","properties":{"events":{"type":"array","items":{"$ref":"#/components/schemas/UsageEvent"},"minItems":1,"maxItems":1000,"description":"Array of usage events (1–1000)"}},"required":["events"],"description":"A batch of usage events to ingest, supporting up to 1000 events per request."},"DryRunResult":{"type":"object","properties":{"object":{"type":"string","enum":["usage_event_dry_run_result"]},"summary":{"type":"object","properties":{"would_ingest":{"type":"number"},"would_skip":{"type":"number"},"would_fail":{"type":"number"}},"required":["would_ingest","would_skip","would_fail"]},"data":{"type":"array","items":{"type":"object","properties":{"index":{"type":"number"},"status":{"type":"string","enum":["would_ingest","would_skip","would_fail"]},"validationDetails":{"type":"object","properties":{"meterMatched":{"type":"boolean"},"customerMatched":{"type":"boolean"},"billableQuantity":{"type":"number"},"filtersPassed":{"type":"boolean"}},"required":["meterMatched","customerMatched","billableQuantity","filtersPassed"]},"reason":{"type":"string"}},"required":["index","status","validationDetails"]}}},"required":["object","summary","data"],"description":"Result of a dry-run ingest showing how events would be matched to meters without persisting them."},"UsageEventListResponse":{"type":"object","properties":{"object":{"type":"string","enum":["list"]},"data":{"type":"array","items":{"$ref":"#/components/schemas/UsageEventRecord"},"description":"Array of usage event objects"},"has_more":{"type":"boolean"}},"required":["object","data","has_more"],"description":"A paginated list of usage events matching the query filters."},"UsageEventRecord":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Usage event ID","example":"8d9e0f1a-2b3c-4641-9e5f-6a7b8c9d0e12"},"merchant_id":{"type":"string","format":"uuid","description":"Owning merchant ID","example":"8f14e45f-ceea-4e7a-9b4b-7c3f1a2d5e60"},"customer_id":{"type":["string","null"],"format":"uuid","description":"Customer ID","example":"c2a1e7d4-5b3f-4a8e-9d21-6f0b3e4c7a11"},"subscription_id":{"type":["string","null"],"format":"uuid","description":"Subscription ID","example":"d4e6f8a1-2b3c-4d5e-9f60-7a8b9c0d1e24"},"meter_id":{"type":["string","null"],"format":"uuid","description":"Meter ID","example":"7c8d9e0f-1a2b-4630-8d4e-5f6a7b8c9d01"},"name":{"type":["string","null"],"description":"Event name (Polar-compatible alias of event_name)"},"event_name":{"type":["string","null"],"description":"Event name that matched the meter"},"quantity":{"type":"number","description":"Event quantity","example":1},"event_time":{"type":"string","format":"date-time","description":"Event timestamp (ISO 8601)"},"idempotency_key":{"type":["string","null"],"description":"Deduplication key"},"external_id":{"type":["string","null"],"description":"External event identifier"},"source":{"type":"string","description":"Ingestion source"},"metadata":{"type":"object","additionalProperties":true,"description":"Arbitrary event metadata"},"properties":{"type":"object","additionalProperties":true,"description":"Arbitrary event properties used for filtering/aggregation"},"ingestion_timestamp":{"type":["string","null"],"format":"date-time","description":"When the event was ingested (ISO 8601)"},"created_at":{"type":"string","format":"date-time","description":"Creation timestamp (ISO 8601)"}},"required":["id","merchant_id","customer_id","subscription_id","meter_id","quantity","event_time","idempotency_key","external_id","source","metadata","properties","ingestion_timestamp","created_at"],"description":"A persisted usage event record.","example":{"id":"8d9e0f1a-2b3c-4641-9e5f-6a7b8c9d0e12","merchant_id":"8f14e45f-ceea-4e7a-9b4b-7c3f1a2d5e60","customer_id":"c2a1e7d4-5b3f-4a8e-9d21-6f0b3e4c7a11","subscription_id":"d4e6f8a1-2b3c-4d5e-9f60-7a8b9c0d1e24","meter_id":"7c8d9e0f-1a2b-4630-8d4e-5f6a7b8c9d01","name":"api_call","event_name":"api_call","quantity":1,"event_time":"2026-04-18T09:15:00.000Z","idempotency_key":"api_call_req_000123","external_id":null,"source":"api","metadata":{},"properties":{"endpoint":"/engines/difference"},"ingestion_timestamp":"2026-04-18T09:15:01.000Z","created_at":"2026-04-18T09:15:01.000Z"}},"UsageAggregateResponse":{"type":"object","properties":{"object":{"type":"string","enum":["usage_aggregate"]},"meter_id":{"type":"string"},"start_time":{"type":"string","format":"date-time"},"end_time":{"type":"string","format":"date-time"},"value":{"type":"number","description":"Aggregated usage value"},"event_count":{"type":"number","description":"Number of events in range"}},"required":["object","meter_id","start_time","end_time","value","event_count"],"description":"Aggregated usage totals grouped by meter and time period."},"UsageEventGetResponse":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/UsageEventRecord"}},"required":["data"],"description":"A single persisted usage event."},"UsageBalanceResponse":{"type":"object","properties":{"object":{"type":"string","enum":["usage_balance"]},"meters":{"type":"array","items":{"$ref":"#/components/schemas/UsageBalanceMeter"}},"total_estimated_amount_minor":{"type":"integer","description":"Total estimated charge across all meters","example":500},"currency":{"type":"string","description":"Currency code","example":"gbp"}},"required":["object","meters","total_estimated_amount_minor","currency"],"description":"Current-period usage balance across all meters for a customer, with per-meter breakdowns and estimated totals.","example":{"object":"usage_balance","meters":[{"meter_id":"7c8d9e0f-1a2b-4630-8d4e-5f6a7b8c9d01","meter_name":"API calls","price_name":"API call overage","unit_name":"calls","billing_scheme":"per_unit","tiers_mode":null,"unit_amount_minor":2,"unit_amount_decimal":"2","package_size":null,"percentage_rate_bps":null,"current_value":1250,"included_quantity":1000,"used_quantity":1250,"remaining_included":0,"overage_quantity":250,"estimated_amount_minor":500,"currency":"gbp","period_start":"2026-04-16T10:00:00.000Z","period_end":"2026-05-16T10:00:00.000Z","last_event_at":"2026-04-18T09:15:00.000Z"}],"total_estimated_amount_minor":500,"currency":"gbp"}},"UsageBalanceMeter":{"type":"object","properties":{"meter_id":{"type":"string","description":"Meter ID","example":"7c8d9e0f-1a2b-4630-8d4e-5f6a7b8c9d01"},"meter_name":{"type":"string","description":"Meter display name","example":"API calls"},"price_name":{"type":["string","null"],"description":"Attached price display name","example":"API call overage"},"unit_name":{"type":["string","null"],"description":"Unit display name","example":"calls"},"billing_scheme":{"type":["string","null"],"description":"Pricing scheme used for estimated charges: per_unit, tiered, or percentage","example":"per_unit"},"tiers_mode":{"type":["string","null"],"description":"Tier mode when billing_scheme is tiered","example":null},"unit_amount_minor":{"type":["integer","null"],"description":"Per-unit amount in minor currency units when applicable. Null when the rate is sub-penny — see `unit_amount_decimal`.","example":2},"unit_amount_decimal":{"type":["string","null"],"description":"Exact per-unit rate in minor units as a decimal string (sub-penny safe, e.g. \"0.02\"). Mirrors Stripe's `unit_amount_decimal`.","example":"2"},"package_size":{"type":["number","null"],"description":"Package size when package-style metered pricing is configured","example":null},"percentage_rate_bps":{"type":["number","null"],"description":"Percentage rate in basis points when billing_scheme is percentage. 70 = 0.7%.","example":null},"current_value":{"type":"number","description":"Aggregated usage in current period","example":1250},"included_quantity":{"type":"number","description":"Units included in plan (0 if pure metered)","example":1000},"used_quantity":{"type":"number","description":"Usage consumed (same as current_value)","example":1250},"remaining_included":{"type":"number","description":"Remaining included units (max 0)","example":0},"overage_quantity":{"type":"number","description":"Units over allowance (max 0)","example":250},"estimated_amount_minor":{"type":"integer","description":"Estimated charge for overage in minor units","example":500},"currency":{"type":"string","description":"Currency code","example":"gbp"},"period_start":{"type":"string","description":"Current billing period start (ISO 8601)","example":"2026-04-16T10:00:00.000Z"},"period_end":{"type":"string","description":"Current billing period end (ISO 8601)","example":"2026-05-16T10:00:00.000Z"},"last_event_at":{"type":["string","null"],"format":"date-time","description":"Most recent usage event timestamp in the current billing period","example":"2026-04-18T09:15:00.000Z"}},"required":["meter_id","meter_name","price_name","unit_name","billing_scheme","tiers_mode","unit_amount_minor","package_size","percentage_rate_bps","current_value","included_quantity","used_quantity","remaining_included","overage_quantity","estimated_amount_minor","currency","period_start","period_end","last_event_at"],"description":"Current-period usage for a single meter, including consumed units, included allowance, overage, and estimated cost.","example":{"meter_id":"7c8d9e0f-1a2b-4630-8d4e-5f6a7b8c9d01","meter_name":"API calls","price_name":"API call overage","unit_name":"calls","billing_scheme":"per_unit","tiers_mode":null,"unit_amount_minor":2,"unit_amount_decimal":"2","package_size":null,"percentage_rate_bps":null,"current_value":1250,"included_quantity":1000,"used_quantity":1250,"remaining_included":0,"overage_quantity":250,"estimated_amount_minor":500,"currency":"gbp","period_start":"2026-04-16T10:00:00.000Z","period_end":"2026-05-16T10:00:00.000Z","last_event_at":"2026-04-18T09:15:00.000Z"}},"UsageBalanceGet400ResponseError":{"type":"object","properties":{"message":{"type":"string"},"type":{"type":"string"}},"required":["message","type"]},"UsageEconomicsResponse":{"type":"object","properties":{"object":{"type":"string","enum":["usage_economics"]},"meters":{"type":"array","items":{"$ref":"#/components/schemas/UsageEconomicsMeter"}},"total_estimated_amount_minor":{"type":"integer","description":"Total estimated charge across all meters","example":500},"total_cost_minor":{"type":["integer","null"],"description":"Total event cost across all meters. Null when no cost data exists.","example":null},"total_payment_processing_cost_minor":{"type":["integer","null"],"description":"Payment-processing cost from transaction_costs for matching paid transactions. Null when no matching settled/paid cost evidence is available.","example":null},"total_platform_fee_minor":{"type":["integer","null"],"description":"RevKeen/platform fee from transaction_costs for matching paid transactions. Null when no matching settled/paid cost evidence is available.","example":null},"projected_net_margin_minor":{"type":["integer","null"],"description":"Total estimated charge minus AI provider COGS, payment-processing cost, and platform fee. Projected: the period has not settled. Null until all cost inputs are available.","example":null},"projected_net_margin_percent":{"type":["number","null"],"description":"projected_net_margin_minor as a percentage of total_estimated_amount_minor. Null until projected net margin is available.","example":null},"currency":{"type":"string","description":"Currency code","example":"gbp"}},"required":["object","meters","total_estimated_amount_minor","total_cost_minor","total_payment_processing_cost_minor","total_platform_fee_minor","projected_net_margin_minor","projected_net_margin_percent","currency"],"description":"Projected cost, fees and net margin across all meters for the current billing period. Dashboard-only.","example":{"object":"usage_economics","meters":[{"meter_id":"7c8d9e0f-1a2b-4630-8d4e-5f6a7b8c9d01","meter_name":"API calls","estimated_amount_minor":500,"total_cost_minor":null,"projected_margin_minor":null,"currency":"gbp","period_start":"2026-04-16T10:00:00.000Z","period_end":"2026-05-16T10:00:00.000Z"}],"total_estimated_amount_minor":500,"total_cost_minor":null,"total_payment_processing_cost_minor":null,"total_platform_fee_minor":null,"projected_net_margin_minor":null,"projected_net_margin_percent":null,"currency":"gbp"}},"UsageEconomicsMeter":{"type":"object","properties":{"meter_id":{"type":"string","description":"Meter ID","example":"7c8d9e0f-1a2b-4630-8d4e-5f6a7b8c9d01"},"meter_name":{"type":"string","description":"Meter display name","example":"API calls"},"estimated_amount_minor":{"type":"integer","description":"Estimated charge for overage in minor units","example":500},"total_cost_minor":{"type":["integer","null"],"description":"Sum of event cost annotations. Null when no cost data exists.","example":null},"projected_margin_minor":{"type":["integer","null"],"description":"Estimated charge minus event cost. Null when no cost data exists.","example":null},"currency":{"type":"string","description":"Currency code","example":"gbp"},"period_start":{"type":"string","description":"Current billing period start (ISO 8601)","example":"2026-04-16T10:00:00.000Z"},"period_end":{"type":"string","description":"Current billing period end (ISO 8601)","example":"2026-05-16T10:00:00.000Z"}},"required":["meter_id","meter_name","estimated_amount_minor","total_cost_minor","projected_margin_minor","currency","period_start","period_end"],"description":"Projected cost and margin for one meter in the current billing period.","example":{"meter_id":"7c8d9e0f-1a2b-4630-8d4e-5f6a7b8c9d01","meter_name":"API calls","estimated_amount_minor":500,"total_cost_minor":null,"projected_margin_minor":null,"currency":"gbp","period_start":"2026-04-16T10:00:00.000Z","period_end":"2026-05-16T10:00:00.000Z"}},"EntitlementListResponse":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Entitlement"}},"pagination":{"type":"object","properties":{"limit":{"type":"number"},"offset":{"type":"number"},"total":{"type":"number"}},"required":["limit","offset","total"]},"customer":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"email":{"type":"string"},"name":{"type":["string","null"]}},"required":["id","email","name"]}},"required":["data","pagination","customer"],"example":{"data":[{"id":"0f1a2b3c-4d5e-4663-8a71-8c9d0e1f2a34","customer_id":"c2a1e7d4-5b3f-4a8e-9d21-6f0b3e4c7a11","benefit_id":"9e0f1a2b-3c4d-4652-af60-7b8c9d0e1f23","benefit":{"id":"9e0f1a2b-3c4d-4652-af60-7b8c9d0e1f23","name":"Priority support","description":"Replies within four working hours.","benefit_type":"custom","benefit_key":"priority_support","category":"support","icon_url":null,"display_order":"1","is_active":true,"default_value":null,"config":null},"granted_at":"2026-04-02T10:05:00.000Z","expires_at":null,"metadata":{},"status":"active","has_access":true,"access_level":"full","subscription_id":"d4e6f8a1-2b3c-4d5e-9f60-7a8b9c0d1e24","subscription_status":"active"}],"pagination":{"limit":50,"offset":0,"total":1},"customer":{"id":"c2a1e7d4-5b3f-4a8e-9d21-6f0b3e4c7a11","email":"ada.lovelace@example.com","name":"Ada Lovelace"}}},"Entitlement":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"customer_id":{"type":"string","format":"uuid"},"benefit_id":{"type":"string","format":"uuid"},"benefit":{"$ref":"#/components/schemas/Benefit"},"granted_at":{"type":"string","format":"date-time"},"expires_at":{"type":["string","null"],"format":"date-time"},"metadata":{"type":"object","additionalProperties":true},"status":{"type":"string","enum":["active","trialing","grace","past_due","restricted","suspended","canceled"]},"has_access":{"type":"boolean"},"access_level":{"type":"string","enum":["full","partial","none"]},"subscription_id":{"type":["string","null"],"format":"uuid"},"subscription_status":{"type":["string","null"]}},"required":["id","customer_id","benefit_id","benefit","granted_at","expires_at","metadata","status","has_access","access_level","subscription_id","subscription_status"],"example":{"id":"0f1a2b3c-4d5e-4663-8a71-8c9d0e1f2a34","customer_id":"c2a1e7d4-5b3f-4a8e-9d21-6f0b3e4c7a11","benefit_id":"9e0f1a2b-3c4d-4652-af60-7b8c9d0e1f23","benefit":{"id":"9e0f1a2b-3c4d-4652-af60-7b8c9d0e1f23","name":"Priority support","description":"Replies within four working hours.","benefit_type":"custom","benefit_key":"priority_support","category":"support","icon_url":null,"display_order":"1","is_active":true,"default_value":null,"config":null},"granted_at":"2026-04-02T10:05:00.000Z","expires_at":null,"metadata":{},"status":"active","has_access":true,"access_level":"full","subscription_id":"d4e6f8a1-2b3c-4d5e-9f60-7a8b9c0d1e24","subscription_status":"active"}},"Benefit":{"type":["object","null"],"properties":{"id":{"type":"string","format":"uuid"},"name":{"type":"string"},"description":{"type":["string","null"]},"benefit_type":{"type":"string"},"benefit_key":{"type":"string"},"category":{"type":["string","null"]},"icon_url":{"type":["string","null"]},"display_order":{"type":["string","null"]},"is_active":{"type":["boolean","null"]},"default_value":{},"config":{}},"required":["id","name","description","benefit_type","benefit_key","category","icon_url","display_order","is_active"],"example":{"id":"9e0f1a2b-3c4d-4652-af60-7b8c9d0e1f23","name":"Priority support","description":"Replies within four working hours.","benefit_type":"custom","benefit_key":"priority_support","category":"support","icon_url":null,"display_order":"1","is_active":true,"default_value":null,"config":null}},"EntitlementCheckResponse":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/EntitlementCheck"}},"required":["data"]},"EntitlementCheck":{"type":"object","properties":{"benefit_key":{"type":"string"},"has_access":{"type":"boolean"},"access_level":{"type":"string","enum":["full","partial","none"]},"status":{"type":["string","null"],"enum":["active","trialing","grace","past_due","restricted","suspended","canceled",null]},"benefit":{"$ref":"#/components/schemas/Benefit"},"reason":{"type":["string","null"]}},"required":["benefit_key","has_access","access_level","status","benefit","reason"],"example":{"benefit_key":"priority_support","has_access":true,"access_level":"full","status":"active","benefit":{"id":"9e0f1a2b-3c4d-4652-af60-7b8c9d0e1f23","name":"Priority support","description":"Replies within four working hours.","benefit_type":"custom","benefit_key":"priority_support","category":"support","icon_url":null,"display_order":"1","is_active":true,"default_value":null,"config":null},"reason":null}},"TransactionListResponse":{"type":"object","properties":{"object":{"type":"string","enum":["list"]},"data":{"type":"array","items":{"$ref":"#/components/schemas/Transaction"}},"has_more":{"type":"boolean"},"total_count":{"type":"integer"}},"required":["object","data","has_more"],"description":"A paginated list of transactions matching the query filters.","example":{"object":"list","data":[{"id":"7b8c9d0e-1f2a-4b43-9d4e-5f6a7b8c9d42","object":"transaction","type":"sale","status":"succeeded","invoice_id":"f1a2b3c4-d5e6-4270-8a9b-0c1d2e3f4a21","customer_id":"c2a1e7d4-5b3f-4a8e-9d21-6f0b3e4c7a11","parent_transaction_id":null,"currency":"gbp","amount":5880,"amount_refunded":1176,"gateway":"nmi","gateway_transaction_id":"10482736591","gateway_response_code":"100","gateway_response_text":"SUCCESS","payment_method_type":"card","card_brand":"visa","card_last_four":"4242","metadata":{},"created_at":"2026-04-16T10:02:00.000Z","updated_at":"2026-04-20T14:31:00.000Z"}],"has_more":false,"total_count":1}},"Transaction":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Unique identifier for the transaction"},"object":{"type":"string","enum":["transaction"],"description":"Object type"},"type":{"type":"string","enum":["sale","refund","void","capture","dispute","adjustment"],"description":"Transaction type. sale: original payment. refund: money returned. void: cancel before settlement. capture: capture a previous auth. dispute: chargeback. adjustment: manual correction."},"status":{"type":"string","enum":["pending","succeeded","failed","voided"],"description":"Transaction status. pending: processing. succeeded: completed. failed: declined or errored. voided: canceled."},"invoice_id":{"type":"string","format":"uuid","description":"Associated invoice ID"},"customer_id":{"type":["string","null"],"format":"uuid","description":"Customer ID"},"parent_transaction_id":{"type":["string","null"],"format":"uuid","description":"Parent transaction ID (required for refunds, voids, disputes, adjustments)"},"currency":{"type":"string","description":"Three-letter ISO currency code"},"amount":{"type":"integer","description":"Transaction amount in minor units (cents)"},"amount_refunded":{"type":"integer","default":0,"description":"Amount refunded in minor units"},"gateway":{"type":["string","null"],"description":"Payment gateway (e.g., 'nmi')"},"gateway_transaction_id":{"type":["string","null"],"description":"Gateway's transaction reference"},"gateway_response_code":{"type":["string","null"],"description":"Gateway response code"},"gateway_response_text":{"type":["string","null"],"description":"Gateway response message"},"payment_method_type":{"type":["string","null"],"description":"Payment method type (card, ach, wallet)"},"card_brand":{"type":["string","null"],"description":"Card brand (visa, mastercard, etc.)"},"card_last_four":{"type":["string","null"],"description":"Last 4 digits of card"},"metadata":{"type":"object","additionalProperties":true,"description":"Custom key-value metadata"},"created_at":{"type":"string","format":"date-time","description":"Creation timestamp (ISO 8601)"},"updated_at":{"type":"string","format":"date-time","description":"Last update timestamp (ISO 8601)"}},"required":["id","object","type","status","invoice_id","currency","amount","created_at","updated_at"],"description":"A financial movement in the unified transaction model. Every money movement — payments, refunds, voids, disputes — is recorded as a transaction linked to an invoice.","example":{"id":"7b8c9d0e-1f2a-4b43-9d4e-5f6a7b8c9d42","object":"transaction","type":"sale","status":"succeeded","invoice_id":"f1a2b3c4-d5e6-4270-8a9b-0c1d2e3f4a21","customer_id":"c2a1e7d4-5b3f-4a8e-9d21-6f0b3e4c7a11","parent_transaction_id":null,"currency":"gbp","amount":5880,"amount_refunded":1176,"gateway":"nmi","gateway_transaction_id":"10482736591","gateway_response_code":"100","gateway_response_text":"SUCCESS","payment_method_type":"card","card_brand":"visa","card_last_four":"4242","metadata":{},"created_at":"2026-04-16T10:02:00.000Z","updated_at":"2026-04-20T14:31:00.000Z"}},"TransactionRetrieveResponse":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Transaction"}},"required":["data"],"description":"A single transaction object."},"ExportCreateResponse":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Export"}},"required":["data"],"description":"The created export job in 'pending' status."},"Export":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Unique export job ID"},"object":{"type":"string","enum":["export"],"description":"Object type"},"status":{"type":"string","enum":["pending","processing","completed","failed"],"description":"Export job status. pending: queued. processing: generating file. completed: ready for download. failed: export failed."},"resource_type":{"type":"string","enum":["customers","invoices","transactions","subscriptions","products","payment_methods"],"description":"The type of resource to export."},"format":{"type":"string","enum":["csv","xlsx"],"description":"Output file format. csv: Comma-separated values. xlsx: Excel workbook."},"download_url":{"type":["string","null"],"format":"uri","description":"Presigned download URL (available when status is 'completed'). Expires after 1 hour."},"total_rows":{"type":["integer","null"],"description":"Total rows exported (available when completed)"},"file_size_bytes":{"type":["integer","null"],"description":"File size in bytes (available when completed)"},"error_message":{"type":["string","null"],"description":"Error message (if status is 'failed')"},"created_at":{"type":"string","format":"date-time","description":"When the export was requested (ISO 8601)"},"completed_at":{"type":["string","null"],"format":"date-time","description":"When the export completed (ISO 8601)"}},"required":["id","object","status","resource_type","format","created_at"],"description":"An async data export job. Create an export, poll for completion, then download the file via the presigned URL."},"CreateExportRequest":{"type":"object","properties":{"resource_type":{"type":"string","enum":["customers","invoices","transactions","subscriptions","products","payment_methods"],"description":"The type of resource to export."},"format":{"type":"string","enum":["csv","xlsx"],"default":"csv","description":"Output file format. csv: Comma-separated values. xlsx: Excel workbook."},"filters":{"type":"object","additionalProperties":true,"description":"Filters to apply (e.g., { status: 'paid', created_gte: '2024-01-01' })"},"column_set":{"type":"string","enum":["standard","full","minimal"],"default":"standard","description":"Column set to include. standard: common fields. full: all fields. minimal: IDs and key fields only."}},"required":["resource_type"],"description":"Parameters for creating an async data export. The export is processed in the background — poll GET /v2/exports/:id for status."},"ExportRetrieveResponse":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Export"}},"required":["data"],"description":"Export job status and details."},"ImportCreateResponse":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Import"}},"required":["data"],"description":"The created import job in 'pending' status."},"Import":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Unique import job ID"},"object":{"type":"string","enum":["import"],"description":"Object type"},"status":{"type":"string","enum":["pending","validating","processing","completed","completed_with_errors","failed"],"description":"Import job status. pending: queued. validating: checking data. processing: creating records. completed: all rows imported. completed_with_errors: some rows failed. failed: import aborted."},"resource_type":{"type":"string","enum":["customers","products","invoices"],"description":"The type of resource to import."},"total_rows":{"type":["integer","null"],"description":"Total rows in the uploaded file"},"processed_rows":{"type":["integer","null"],"description":"Number of rows processed so far"},"success_count":{"type":["integer","null"],"description":"Number of rows successfully imported"},"error_count":{"type":["integer","null"],"description":"Number of rows that failed"},"errors":{"type":["array","null"],"items":{"type":"object","properties":{"row":{"type":"integer","description":"Row number (1-based)"},"field":{"type":"string","description":"Field that caused the error"},"message":{"type":"string","description":"Error message"}},"required":["row","message"]},"description":"Detailed error information for failed rows"},"created_at":{"type":"string","format":"date-time","description":"When the import was created (ISO 8601)"},"completed_at":{"type":["string","null"],"format":"date-time","description":"When the import completed (ISO 8601)"}},"required":["id","object","status","resource_type","created_at"],"description":"An async data import job. Upload a CSV file to create records in bulk. Poll GET /v2/imports/:id for status and error details."},"CreateImportRequest":{"type":"object","properties":{"resource_type":{"type":"string","enum":["customers","products","invoices"],"description":"The type of resource to import."},"file_url":{"type":"string","format":"uri","description":"URL to the CSV file to import (if not using multipart upload)"},"column_mapping":{"type":"object","additionalProperties":true,"description":"Map CSV column names to resource fields (e.g., { 'Email Address': 'email', 'Full Name': 'name' })"},"on_duplicate":{"type":"string","enum":["skip","update","error"],"default":"skip","description":"Behavior when a duplicate record is found. skip: ignore the row. update: merge new data. error: fail the row."}},"required":["resource_type"],"description":"Parameters for creating a bulk data import. Either provide a file_url or upload the CSV via multipart form data."},"ImportRetrieveResponse":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Import"}},"required":["data"],"description":"Import job status and progress."},"DdMandateRequestCreateResponse":{"type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string"},"merchant_id":{"type":"string"},"customer_id":{"type":"string"},"invoice_id":{"type":["string","null"]},"token":{"type":"string","description":"Signed mandate-request token for the emailed link"},"signable_url":{"type":"string","description":"Customer-facing URL that opens the sign flow"},"created_at":{"type":["string","null"],"format":"date-time"},"expires_at":{"type":["string","null"],"format":"date-time"},"status":{"type":"string","example":"pending_signature"},"idempotent":{"type":"boolean","description":"True when an existing live request was returned instead of creating a new one"}},"required":["id","merchant_id","customer_id","invoice_id","token","signable_url","created_at","expires_at","status","idempotent"]}},"required":["data"]},"DdMandateRequestActiveResponse":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/DdMandateRequestActiveItem"}},"required":["data"]},"DdMandateRequestActiveItem":{"allOf":[{"$ref":"#/components/schemas/DdMandateRequestLifecycleItem"},{"type":["object","null"],"properties":{"opened_at":{"type":["string","null"],"format":"date-time","description":"When the customer first opened the request link (its first capture session). Null until opened."}},"required":["opened_at"]}]},"DdMandateRequestLifecycleStatus":{"type":"string","enum":["draft","pending_signature","consumed","expired","cancelled","failed"]},"DdMandatePdfReference":{"type":"object","properties":{"id":{"type":"string"},"mandate_request_id":{"type":["string","null"]},"mandate_id":{"type":"string"},"customer_id":{"type":"string"},"merchant_id":{"type":"string"},"pdf_type":{"type":"string","example":"signed_mandate_confirmation"},"storage_key":{"type":"string"},"pdf_url":{"type":["string","null"]},"generated_at":{"type":"string","format":"date-time"},"template_version":{"type":"string"},"compliance_version":{"type":"string"}},"required":["id","mandate_request_id","mandate_id","customer_id","merchant_id","pdf_type","storage_key","pdf_url","generated_at","template_version","compliance_version"]},"DdMandateRequestLifecycleItem":{"type":"object","properties":{"id":{"type":"string"},"merchant_id":{"type":"string"},"customer_id":{"type":"string"},"invoice_id":{"type":["string","null"]},"subscription_id":{"type":["string","null"]},"mandate_id":{"type":["string","null"]},"token":{"type":"string","description":"Internal raw request token. Do not expose directly to customers."},"signable_url":{"type":"string","description":"Customer-facing tracked URL: pay.revkeen.com/dd_<token>"},"created_at":{"type":["string","null"],"format":"date-time"},"expires_at":{"type":["string","null"],"format":"date-time"},"consumed_at":{"type":["string","null"],"format":"date-time"},"cancelled_at":{"type":["string","null"],"format":"date-time"},"expired_at":{"type":["string","null"],"format":"date-time"},"status":{"$ref":"#/components/schemas/DdMandateRequestLifecycleStatus"},"notification_delivery_event_type":{"type":["string","null"]},"notification_delivery_status":{"type":["string","null"]},"notification_delivery_error":{"type":["string","null"]},"notification_delivery_updated_at":{"type":["string","null"],"format":"date-time"},"pdf_documents":{"type":"array","items":{"$ref":"#/components/schemas/DdMandatePdfReference"}}},"required":["id","merchant_id","customer_id","invoice_id","subscription_id","mandate_id","token","signable_url","created_at","expires_at","consumed_at","cancelled_at","expired_at","status","notification_delivery_event_type","notification_delivery_status","notification_delivery_error","notification_delivery_updated_at","pdf_documents"]},"DdMandateRequestHistoryResponse":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/DdMandateRequestLifecycleItem"}}},"required":["data"]},"MandateRequestLookupResponse":{"type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string","description":"Mandate request ID"},"merchant_id":{"type":"string","description":"Merchant ID"},"merchant_slug":{"type":"string","description":"Merchant slug"},"customer_id":{"type":"string","description":"Customer ID"},"invoice_id":{"type":["string","null"],"description":"Invoice ID, if the request is tied to an invoice"},"expires_at":{"type":["string","null"],"format":"date-time","description":"Expiry timestamp (ISO 8601)"},"status":{"type":"string","description":"Mandate request status","example":"pending_signature"},"merchant":{"type":"object","properties":{"id":{"type":"string"},"slug":{"type":"string"},"name":{"type":"string"}},"required":["id","slug","name"],"description":"Merchant display details"},"customer":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":["string","null"]},"first_name":{"type":"string"},"last_name":{"type":"string"},"email":{"type":["string","null"]},"phone":{"type":["string","null"]}},"required":["id","name","first_name","last_name","email","phone"],"description":"Customer details for prefilling the mandate form"},"invoice":{"type":["object","null"],"properties":{"id":{"type":"string"},"invoice_number":{"type":["string","null"]},"total":{"type":["string","null"]},"amount_due":{"type":["string","null"]},"currency":{"type":["string","null"]}},"required":["id","invoice_number","total","amount_due","currency"],"description":"Invoice details, if the request is tied to an invoice"}},"required":["id","merchant_id","merchant_slug","customer_id","invoice_id","expires_at","status","merchant","customer","invoice"]}},"required":["data"],"description":"Public lookup of a pending signed mandate request.","example":{"data":{"id":"2c3d4e5f-6a7b-4c98-8da9-1e2f3a4b5c97","merchant_id":"8f14e45f-ceea-4e7a-9b4b-7c3f1a2d5e60","merchant_slug":"example-studio","customer_id":"c2a1e7d4-5b3f-4a8e-9d21-6f0b3e4c7a11","invoice_id":null,"expires_at":"2026-04-09T10:05:00.000Z","status":"pending_signature","merchant":{"id":"8f14e45f-ceea-4e7a-9b4b-7c3f1a2d5e60","slug":"example-studio","name":"Example Studio"},"customer":{"id":"c2a1e7d4-5b3f-4a8e-9d21-6f0b3e4c7a11","name":"Ada Lovelace","first_name":"Ada","last_name":"Lovelace","email":"ada.lovelace@example.com","phone":"+44 20 7946 0958"},"invoice":null}}},"DdMandateRequestConsumeResponse":{"type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string"},"customer_id":{"type":"string"},"invoice_id":{"type":["string","null"]},"mandate_id":{"type":["string","null"]},"consumed_at":{"type":["string","null"],"format":"date-time"},"status":{"type":"string","example":"consumed"},"idempotent":{"type":"boolean"},"pdf_documents":{"type":"array","items":{"$ref":"#/components/schemas/DdMandatePdfReference"}}},"required":["id","customer_id","invoice_id","mandate_id","consumed_at","status"]}},"required":["data"]},"DdMandateRequestCancelResponse":{"type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string"},"customer_id":{"type":"string"},"invoice_id":{"type":["string","null"]},"status":{"type":"string","example":"cancelled"},"cancelled_at":{"type":["string","null"],"format":"date-time"},"cancelled_by":{"type":["string","null"]},"expired_at":{"type":["string","null"],"format":"date-time"},"reason":{"type":["string","null"]},"idempotent":{"type":"boolean"}},"required":["id","customer_id","invoice_id","status"]}},"required":["data"]},"DdCaptureSessionResponse":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/DdCaptureSession"}},"required":["data"]},"DdCaptureSession":{"type":"object","properties":{"id":{"type":"string"},"session_token":{"type":"string","description":"Plaintext capture-session token. Only present in the response that issues it (create / token re-issue); never returned on subsequent reads."},"merchant_id":{"type":"string"},"customer_id":{"type":["string","null"]},"checkout_session_id":{"type":["string","null"]},"mandate_request_id":{"type":["string","null"]},"source":{"type":"string","example":"checkout"},"status":{"type":"string","example":"bank_details_pending"},"account_holder_name":{"type":["string","null"]},"sort_code_last2":{"type":["string","null"],"description":"Last 2 digits of the sort code — never the full value"},"account_number_last4":{"type":["string","null"],"description":"Last 4 digits of the account number"},"bank_name":{"type":["string","null"]},"notice_date":{"type":["string","null"],"description":"Advance-notice date (YYYY-MM-DD)"},"submission_date":{"type":["string","null"],"description":"Bacs submission date (YYYY-MM-DD)"},"collection_date":{"type":["string","null"],"description":"Collection date (YYYY-MM-DD)"},"settlement_date":{"type":["string","null"],"description":"Settlement date (YYYY-MM-DD)"},"notice_days":{"type":["integer","null"]},"mandate_id":{"type":["string","null"],"description":"Mandate created from this capture session, once completed"},"expires_at":{"type":["string","null"],"format":"date-time"},"completed_at":{"type":["string","null"],"format":"date-time"},"abandoned_at":{"type":["string","null"],"format":"date-time"},"created_at":{"type":["string","null"],"format":"date-time"},"updated_at":{"type":["string","null"],"format":"date-time"}},"required":["id","merchant_id","customer_id","checkout_session_id","mandate_request_id","source","status","account_holder_name","sort_code_last2","account_number_last4","bank_name","notice_date","submission_date","collection_date","settlement_date","notice_days","mandate_id","expires_at","completed_at","abandoned_at","created_at","updated_at"]},"ValidateBankAccountResponse":{"type":"object","properties":{"valid":{"type":"boolean","description":"Whether the account details passed modulus/sort-code validation"},"bank_name":{"type":["string","null"],"description":"Resolved bank name, if known","example":"Barclays"},"dd_enabled":{"type":"boolean","description":"Whether the account accepts Direct Debit instructions"},"errors":{"type":["array","null"],"items":{"type":"string"},"description":"Validation error messages, if any"}},"required":["valid","bank_name","dd_enabled"],"description":"Result of a Direct Debit bank-account validation check.","example":{"valid":true,"bank_name":"Example Bank","dd_enabled":true,"errors":null}},"ValidateBankAccountRequest":{"type":"object","properties":{"sortCode":{"type":"string","minLength":6,"maxLength":8,"description":"UK sort code (6 digits, dashes optional)","example":"56-00-03"},"accountNumber":{"type":"string","minLength":8,"maxLength":8,"description":"UK account number (8 digits)","example":"13354647"}},"required":["sortCode","accountNumber"],"description":"Parameters for validating a UK bank account for Direct Debit eligibility."},"PreviewCollectionResponse":{"type":"object","properties":{"notice_date":{"type":"string","description":"Advance-notice date (YYYY-MM-DD)","example":"2026-06-20"},"submission_date":{"type":"string","description":"Bacs submission date (YYYY-MM-DD)"},"collection_date":{"type":"string","description":"Collection date (YYYY-MM-DD)"},"settlement_date":{"type":"string","description":"Settlement date (YYYY-MM-DD)"},"eligible":{"type":"boolean","description":"Whether the requested collection is eligible"},"ineligible_reason":{"type":["string","null"],"description":"Reason the collection is ineligible, if applicable"},"notice_days":{"type":"integer","description":"Advance-notice days applied","example":10}},"required":["notice_date","submission_date","collection_date","settlement_date","eligible","ineligible_reason","notice_days"],"description":"Backend-owned Direct Debit collection date chain (notice/submission/collection/settlement).","example":{"notice_date":"2026-05-01","submission_date":"2026-05-13","collection_date":"2026-05-18","settlement_date":"2026-05-18","eligible":true,"ineligible_reason":null,"notice_days":10}},"PreviewCollectionRequest":{"type":"object","properties":{"requestedCollectionDate":{"type":"string","description":"Preferred collection date (ISO date). Backend resolves the working-day chain.","example":"2026-07-01"},"subscriptionId":{"type":"string","format":"uuid","description":"Subscription the collection is previewed for, if any"}},"description":"Parameters for previewing the Direct Debit notice → collection → settlement date chain."},"MandateResponse":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Mandate"}},"required":["data"],"description":"A single Direct Debit mandate.","example":{"data":{"id":"6a7b8c9d-0e1f-4a32-8c3d-4e5f6a7b8c31","mandate_ref":"RKEXAM01","mandate_reference":"RKEXAM01","customer_id":"c2a1e7d4-5b3f-4a8e-9d21-6f0b3e4c7a11","status":"active","account_holder_name":"Ada Lovelace","sort_code":"XX-XX-**","account_number_last4":"0000","bank_name":"Example Bank","statement_name":"REVKEEN PRO","notice_days":10,"first_collection_date":"2026-05-18","next_collection_date":"2026-05-18","backup_payment_method_id":"9e8d7c6b-5a4f-4e3d-8c2b-1a0f9e8d7c65","activated_at":"2026-04-07T09:00:00.000Z","suspended_at":null,"cancelled_at":null,"failure_reason":null,"created_at":"2026-04-02T10:10:00.000Z","updated_at":"2026-04-07T09:00:00.000Z"}}},"Mandate":{"type":"object","properties":{"id":{"type":"string","description":"Mandate ID"},"mandate_ref":{"type":"string","description":"RevKeen mandate reference (Bacs DDI reference)"},"mandate_reference":{"type":["string","null"],"description":"Human-facing mandate reference"},"customer_id":{"type":"string","description":"Customer the mandate belongs to"},"status":{"type":"string","description":"Mandate status","example":"pending_lodgement"},"account_holder_name":{"type":["string","null"],"description":"Name on the bank account"},"sort_code":{"type":"string","description":"Masked sort code — never the full value","example":"XX-XX-00"},"account_number_last4":{"type":["string","null"],"description":"Last 4 digits of the account number"},"bank_name":{"type":["string","null"],"description":"Resolved bank name"},"statement_name":{"type":["string","null"],"description":"Bacs statement descriptor (max 18 chars)"},"notice_days":{"type":["integer","null"],"description":"Advance-notice days applied"},"first_collection_date":{"type":["string","null"],"description":"Earliest collection date (YYYY-MM-DD)"},"next_collection_date":{"type":["string","null"],"description":"Next scheduled collection date (YYYY-MM-DD)"},"backup_payment_method_id":{"type":["string","null"],"description":"Recovery fallback card"},"activated_at":{"type":["string","null"],"format":"date-time","description":"When the mandate became active (ISO 8601)"},"suspended_at":{"type":["string","null"],"format":"date-time","description":"When the mandate was suspended (ISO 8601)"},"cancelled_at":{"type":["string","null"],"format":"date-time","description":"When the mandate was cancelled (ISO 8601)"},"failure_reason":{"type":["string","null"],"description":"Most recent failure reason"},"mandate_request_id":{"type":["string","null"],"description":"Mandate-request consumed on creation, if any"},"invoice_id":{"type":["string","null"],"description":"Invoice linked via the mandate-request, if any"},"pdf_documents":{"type":"array","items":{"$ref":"#/components/schemas/DdMandatePdfReference"}},"created_at":{"type":["string","null"],"format":"date-time","description":"Creation timestamp (ISO 8601)"},"updated_at":{"type":["string","null"],"format":"date-time","description":"Last-updated timestamp (ISO 8601)"}},"required":["id","mandate_ref","mandate_reference","customer_id","status","account_holder_name","sort_code","account_number_last4","bank_name","notice_days"],"description":"A Direct Debit mandate (customer authorisation to collect via Bacs).","example":{"id":"6a7b8c9d-0e1f-4a32-8c3d-4e5f6a7b8c31","mandate_ref":"RKEXAM01","mandate_reference":"RKEXAM01","customer_id":"c2a1e7d4-5b3f-4a8e-9d21-6f0b3e4c7a11","status":"active","account_holder_name":"Ada Lovelace","sort_code":"XX-XX-**","account_number_last4":"0000","bank_name":"Example Bank","statement_name":"REVKEEN PRO","notice_days":10,"first_collection_date":"2026-05-18","next_collection_date":"2026-05-18","backup_payment_method_id":"9e8d7c6b-5a4f-4e3d-8c2b-1a0f9e8d7c65","activated_at":"2026-04-07T09:00:00.000Z","suspended_at":null,"cancelled_at":null,"failure_reason":null,"created_at":"2026-04-02T10:10:00.000Z","updated_at":"2026-04-07T09:00:00.000Z"}},"CreateMandateRequest":{"type":"object","properties":{"customerId":{"type":"string","format":"uuid","description":"Customer the mandate is created for"},"accountHolderName":{"type":"string","minLength":1,"maxLength":100,"description":"Name on the bank account","example":"Jane Smith"},"sortCode":{"type":"string","minLength":6,"maxLength":8,"description":"UK sort code (6 digits, dashes optional)","example":"56-00-03"},"accountNumber":{"type":"string","minLength":8,"maxLength":8,"description":"UK account number (8 digits)","example":"13354647"},"backupPaymentMethodId":{"type":"string","format":"uuid","description":"Stored card used as recovery fallback if a collection fails"},"requestToken":{"type":"string","minLength":24,"maxLength":160,"description":"Signed mandate-request token to consume on creation"},"acceptance":{"type":"object","properties":{"acceptedAt":{"type":"string","format":"date-time"},"payerAddress":{"type":"object","additionalProperties":{}},"guaranteeVersion":{"type":"string"},"declarations":{"type":"object","additionalProperties":{"type":"boolean"}}},"required":["acceptedAt","payerAddress","guaranteeVersion","declarations"],"description":"Deprecated compatibility field; ignored for authorization evidence"}},"required":["customerId","accountHolderName","sortCode","accountNumber"],"description":"Parameters for creating a Direct Debit mandate. Bank details are validated with the bureau and stored KMS-encrypted; only masked values are ever returned."},"MandateRetrieveValidationError":{"type":"object","properties":{"error":{"type":"object","properties":{"type":{"type":"string","enum":["invalid_request_error"]},"code":{"type":"string","enum":["parameter_invalid"]},"message":{"type":"string"},"request_id":{"type":"string"},"param":{"type":"string","enum":["id"],"description":"The query parameter that was rejected."}},"required":["type","code","message","param"]}},"required":["error"],"description":"Returned when a query parameter fails validation. `param` names the parameter to fix."},"MandateListResponse":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/MandateListItem"}}},"required":["data"],"description":"A list of Direct Debit mandates.","example":{"data":[{"id":"6a7b8c9d-0e1f-4a32-8c3d-4e5f6a7b8c31","mandate_ref":"RKEXAM01","mandate_reference":"RKEXAM01","customer_id":"c2a1e7d4-5b3f-4a8e-9d21-6f0b3e4c7a11","status":"active","account_holder_name":"Ada Lovelace","sort_code":"XX-XX-**","account_number_last4":"0000","bank_name":"Example Bank","notice_days":10,"activated_at":"2026-04-07T09:00:00.000Z","created_at":"2026-04-02T10:10:00.000Z"}]}},"MandateListItem":{"type":"object","properties":{"id":{"type":"string"},"mandate_ref":{"type":"string"},"mandate_reference":{"type":["string","null"]},"customer_id":{"type":"string"},"status":{"type":"string"},"account_holder_name":{"type":["string","null"]},"sort_code":{"type":"string"},"account_number_last4":{"type":["string","null"]},"bank_name":{"type":["string","null"]},"notice_days":{"type":["integer","null"]},"activated_at":{"type":["string","null"],"format":"date-time"},"created_at":{"type":["string","null"],"format":"date-time"}},"required":["id","mandate_ref","mandate_reference","customer_id","status","account_holder_name","sort_code","account_number_last4","bank_name","notice_days","activated_at","created_at"],"description":"Summary representation of a Direct Debit mandate in a list.","example":{"id":"6a7b8c9d-0e1f-4a32-8c3d-4e5f6a7b8c31","mandate_ref":"RKEXAM01","mandate_reference":"RKEXAM01","customer_id":"c2a1e7d4-5b3f-4a8e-9d21-6f0b3e4c7a11","status":"active","account_holder_name":"Ada Lovelace","sort_code":"XX-XX-**","account_number_last4":"0000","bank_name":"Example Bank","notice_days":10,"activated_at":"2026-04-07T09:00:00.000Z","created_at":"2026-04-02T10:10:00.000Z"}},"MandateListValidationError":{"type":"object","properties":{"error":{"type":"object","properties":{"type":{"type":"string","enum":["invalid_request_error"]},"code":{"type":"string","enum":["parameter_invalid"]},"message":{"type":"string"},"request_id":{"type":"string"},"param":{"type":"string","enum":["customerId","status"],"description":"The query parameter that was rejected."}},"required":["type","code","message","param"]}},"required":["error"],"description":"Returned when a query parameter fails validation. `param` names the parameter to fix."},"MandateActionResponse":{"type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string","description":"Mandate ID"},"status":{"type":"string","description":"Resulting mandate status","example":"cancelled"},"mandate_reference":{"type":["string","null"],"description":"Mandate reference (reinstate)"}},"required":["id","status"]}},"required":["data"],"description":"Result of a mandate lifecycle action."},"MandateActionRequest":{"type":"object","properties":{"reason":{"type":"string","description":"Optional reason recorded on the audit trail","example":"customer_request"}},"description":"Optional parameters for a mandate lifecycle action (cancel / suspend / reinstate)."},"ScheduleCollectionResponse":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/CollectionSchedule"}},"required":["data"],"description":"Result of scheduling a one-off Direct Debit collection.","example":{"data":{"id":"4e5f6a7b-8c9d-4eba-afcb-3a4b5c6d7e19","mandate_id":"6a7b8c9d-0e1f-4a32-8c3d-4e5f6a7b8c31","lz_schedule_id":"LZ-SCH-EXAMPLE-0057","amount_minor":5880,"currency":"GBP","source_type":"invoice","source_id":"3d4e5f6a-7b8c-4da9-9eba-2f3a4b5c6d08","status":"scheduled","collection_date":"2026-05-18","notice_date":"2026-05-01","submission_date":"2026-05-13","already_scheduled":false}}},"CollectionSchedule":{"type":"object","properties":{"id":{"type":["string","null"],"description":"Collection schedule ID"},"mandate_id":{"type":"string","description":"Mandate the collection is scheduled against"},"lz_schedule_id":{"type":"string","description":"Bureau schedule reference"},"amount_minor":{"type":"integer","description":"Collection amount in minor units (pence)"},"currency":{"type":"string","description":"ISO 4217 currency","example":"GBP"},"source_type":{"type":"string","description":"invoice | payment_link"},"source_id":{"type":"string","description":"Invoice or payment-link ID the collection settles"},"status":{"type":"string","description":"Schedule status","example":"scheduled"},"collection_date":{"type":["string","null"],"description":"Collection date (YYYY-MM-DD)"},"notice_date":{"type":["string","null"],"description":"Advance-notice date (YYYY-MM-DD)"},"submission_date":{"type":["string","null"],"description":"Bacs submission date (YYYY-MM-DD)"},"already_scheduled":{"type":"boolean","description":"True when an existing schedule for this source was returned instead of creating a duplicate"}},"required":["id","mandate_id","lz_schedule_id","amount_minor","currency","source_type","source_id","status","collection_date","notice_date","submission_date","already_scheduled"],"description":"A scheduled one-off Direct Debit collection.","example":{"id":"4e5f6a7b-8c9d-4eba-afcb-3a4b5c6d7e19","mandate_id":"6a7b8c9d-0e1f-4a32-8c3d-4e5f6a7b8c31","lz_schedule_id":"LZ-SCH-EXAMPLE-0057","amount_minor":5880,"currency":"GBP","source_type":"invoice","source_id":"3d4e5f6a-7b8c-4da9-9eba-2f3a4b5c6d08","status":"scheduled","collection_date":"2026-05-18","notice_date":"2026-05-01","submission_date":"2026-05-13","already_scheduled":false}},"ScheduleCollectionRequest":{"type":"object","properties":{"amountMinor":{"type":"integer","exclusiveMinimum":0,"description":"Collection amount in minor units (pence)","example":2500},"currency":{"type":"string","minLength":3,"maxLength":3,"description":"ISO 4217 currency. Bacs collections are GBP-only.","example":"GBP"},"sourceType":{"type":"string","enum":["invoice"],"description":"What the collection settles. Only an invoice can be collected through this endpoint; the settlement webhook resolves invoices by this reference."},"sourceId":{"type":"string","format":"uuid","description":"Invoice ID the collection is for"},"requestedCollectionDate":{"type":"string","description":"Earliest acceptable collection date (ISO date). The backend resolves the Bacs working-day chain; must be on/after the bureau's earliest allowed date.","example":"2026-07-01"}},"required":["amountMinor","currency","sourceType","sourceId"],"description":"Parameters for scheduling a one-off Direct Debit collection against an active mandate. L&Z confirms the permitted collection date; nothing is marked paid until the bureau confirms the collection outcome."}},"parameters":{}},"tags":[{"name":"Automations","description":"Saved Assistant automations and queued runs. Use these endpoints to list automations, retrieve the current immutable definition, create new automations, and queue manual executions.\n\n**Required Scopes:**\n- `automations:read` - List and retrieve automations\n- `automations:write` - Create automations and queue runs","x-displayName":"Automations"},{"name":"Payment Intents","description":"Payment Intents represent the intent to collect payment. They support 3DS/SCA authentication, manual capture, and provide a client secret for frontend confirmation.\n\n**Required Scopes:**\n- `payment_intents:read` - List and retrieve payment intents\n- `payment_intents:write` - Create, confirm, capture, cancel payment intents\n\n**MCP Tools:** `payments_list`, `payments_get`, `payments_capture`","x-displayName":"Payment Intents"},{"name":"Setup Intents","description":"Setup Intents save payment methods WITHOUT charging. Use them to collect card details for future use (subscriptions, saved cards) or to verify cards with 3DS/SCA before storing.\n\n**Required Scopes:**\n- `setup_intents:read` - List and retrieve setup intents\n- `setup_intents:write` - Create, confirm, cancel setup intents\n\n**MCP Tools:** Available via `customers_*` tools","x-displayName":"Setup Intents"},{"name":"Payment Methods","description":"Payment Methods represent a customer's saved payment instrument (card, bank account, etc.). Attach, detach, and manage payment methods for customers.\n\n**Required Scopes:**\n- `payment_methods:read` - List and retrieve payment methods\n- `payment_methods:write` - Attach, detach, update payment methods","x-displayName":"Payment Methods"},{"name":"Payment Links","description":"Create shareable payment links for invoices, subscriptions, or custom amounts. Customers complete payment on a hosted page.\n\n**Required Scopes:**\n- `payment_links:read` - List and retrieve payment links\n- `payment_links:write` - Create and update payment links","x-displayName":"Payment Links"},{"name":"Checkout Sessions","description":"Create hosted checkout pages to collect payment for an invoice. Redirect customers to RevKeen-hosted payment forms.\n\n**Required Scopes:**\n- `checkout:read` - List and retrieve sessions\n- `checkout:write` - Create checkout sessions","x-displayName":"Checkout Sessions"},{"name":"Cart Sessions","description":"Stage a purchase before the customer reaches the checkout page. Cart Sessions hold line items, add-on selections, and an optional discount code, then convert atomically into a Checkout Session. They are the only path that produces an Invoice, Subscription, or Order from cart-like state.\n\n**Lifecycle:** `open` → `converted` | `abandoned` | `expired`\n\n**Required Scopes:**\n- `cart:read` - Retrieve cart sessions\n- `cart:write` - Create, mutate, and convert cart sessions\n\nSee [Cart Sessions docs](https://docs.revkeen.com/docs/using-revkeen/checkout/cart) for the lifecycle, convert semantics, and abandonment sweep behaviour.","x-displayName":"Cart Sessions"},{"name":"Cart API Keys","description":"Managed publishable and secret API keys for RevKeen Cart integrations. Dashboard-only: these endpoints are not available to API keys. Use the Dashboard or `revkeen cart keys issue` (signed in with `revkeen login`) to create missing managed keys.\n\n**Required Scopes:**\n- `apps:write` - Manage RevKeen Cart API keys","x-displayName":"Cart API Keys"},{"name":"Storefront","description":"Read storefront products and availability, manage browser origin allowlists, and inspect integration readiness for RevKeen Cart.\n\n**Required Scopes:**\n- `products:read` - Read storefront products and availability\n- `apps:read` - Inspect storefront integration status\n- `apps:write` - Manage allowed storefront origins","x-displayName":"Storefront"},{"name":"Charges","description":"Create one-time charges for existing customers. Use for setup fees, overages, or ad-hoc payments.\n\n**Required Scopes:**\n- `charges:read` - List and retrieve charges\n- `charges:write` - Create charges","x-displayName":"Charges"},{"name":"Payments","description":"Payment objects represent completed or in-progress money movements. Use them to capture authorized funds, void pending authorizations, and retrieve transaction details across all payment channels (online, terminal, and recurring).\n\n**Required Scopes:**\n- `payments:read` - List and retrieve payments\n- `payments:write` - Capture, void payments\n\n**MCP Tools:** `payments_list`, `payments_get`, `payments_capture`, `payments_void`","x-displayName":"Payments"},{"name":"Invoices","description":"Invoices are the canonical record of what a customer owes or has paid. Every subscription cycle generates an invoice automatically, and you can also create one-off invoices for ad-hoc charges. Invoices progress through a lifecycle: `draft` → `open` → `finalized` → `paid`.\n\n**Required Scopes:**\n- `invoices:read` - List and retrieve invoices\n- `invoices:write` - Create, update, send, finalize invoices\n\n**MCP Tools:** `invoices_list`, `invoices_get`, `invoices_create`, `invoices_send`, `invoices_finalize`","x-displayName":"Invoices"},{"name":"Subscriptions","description":"Subscriptions manage recurring billing cycles for your customers. Create subscriptions to automatically charge customers on a schedule, with support for trials, prorations, plan changes, and dunning for failed payments.\n\n**Required Scopes:**\n- `subscriptions:read` - List and retrieve subscriptions\n- `subscriptions:write` - Create, update, cancel subscriptions\n\n**MCP Tools:** `subscriptions_get`","x-displayName":"Subscriptions"},{"name":"Subscription Schedules","description":"Schedule future changes to subscriptions. Define phases with different pricing, quantities, or plans that take effect at specified dates.\n\n**Required Scopes:**\n- `subscription_schedules:read` - List and retrieve schedules\n- `subscription_schedules:write` - Create, update, release schedules","x-displayName":"Subscription Schedules"},{"name":"Orders","description":"One-off purchases, created when a checkout is fulfilled, with lifecycle operations: update, cancel, and fulfill.\n\n**Required Scopes:**\n- `orders:read` - List and retrieve orders\n- `orders:write` - Update, cancel, fulfill orders\n\n**MCP Tools:** `orders_list`, `orders_get`, `orders_update`, `orders_cancel`, `orders_fulfill`","x-displayName":"Orders"},{"name":"Billing","description":"Billing utilities let you preview upcoming charges and billing schedules before they execute. Use these endpoints to show customers what they will be charged and when, or to validate pricing changes before applying them.\n\n**Required Scopes:**\n- `invoices:read` - Preview billing schedules","x-displayName":"Billing Utilities"},{"name":"Products","description":"Products represent the goods or services you sell. Each product can have multiple prices for different currencies, billing intervals, or pricing models. Products are used across subscriptions, one-off charges, and checkout sessions.\n\n**Required Scopes:**\n- `products:read` - List and retrieve products\n- `products:write` - Create, update, archive products","x-displayName":"Products"},{"name":"Prices","description":"Prices define how much and how often to charge for a product. A single product can have multiple prices — different currencies, billing intervals, or pricing tiers. Supports fixed-amount, pay-what-you-want, tiered, and free pricing models.\n\n**Required Scopes:**\n- `prices:read` - List and retrieve prices\n- `prices:write` - Create, update, archive prices","x-displayName":"Prices"},{"name":"Discounts","description":"Create discount codes for percentage or fixed amount off. Set usage limits and expiration dates.\n\n**Required Scopes:**\n- `discounts:read` - List and retrieve discounts\n- `discounts:write` - Create, update, delete discounts","x-displayName":"Discounts"},{"name":"Customers","description":"Customer objects represent the individuals or businesses you bill. Use them to store contact information, attach payment methods, and track billing history across subscriptions and one-off payments. Every charge, invoice, and subscription belongs to a customer.\n\n**Required Scopes:**\n- `customers:read` - List and retrieve customers\n- `customers:write` - Create and update customers\n\n**MCP Tools:** `customers_list`, `customers_get`, `customers_create`, `customers_update`","x-displayName":"Customers"},{"name":"Entitlements","description":"Entitlements grant customers access to features, benefits, or content tied to their subscription or purchase. Use these endpoints to list granted entitlements for a customer and to check whether a customer currently has access to a specific benefit.\n\n**Required Scopes:**\n- `customers:read` - List and check customer entitlements","x-displayName":"Entitlements"},{"name":"Payment Rails","description":"Cross-rail intelligence for invoices and customers: per-invoice fee and net-after-fee estimates, per-customer rail availability and smart-pick recommendations. These are dashboard-only and are not available to API keys.","x-displayName":"Payment Rails"},{"name":"Direct Debit","description":"Direct Debit (Bacs) rail operations — validate a UK bank account for DD eligibility and preview the backend-owned collection date chain (advance notice → submission → collection → settlement). Clients never compute these dates.\n\n**Required Scopes:**\n- `mandates:read` - Validate and preview","x-displayName":"Direct Debit"},{"name":"Mandates","description":"Direct Debit (Bacs) mandate lifecycle. Create and manage Bacs Direct Debit mandates, including AUDDIS submission, suspension, reinstatement, and cancellation.\n\n**Required Scopes:**\n- `mandates:read` - List and retrieve mandates\n- `mandates:write` - Schedule one-off collections, authorised as the member who issued the key (an owner, admin or finance role; the key cannot do more than its issuer may). Create, suspend, reinstate, cancel mandates","x-displayName":"Mandates"},{"name":"Refunds","description":"Refund objects represent money returned to a customer after a completed payment. You can issue full or partial refunds, and they automatically create a child transaction linked to the original payment for audit trail purposes.\n\n**Required Scopes:**\n- `refunds:read` - List and retrieve refunds\n- `refunds:write` - Create refunds\n\n**MCP Tools:** `refunds_create`","x-displayName":"Refunds"},{"name":"Voids","description":"Cancel unsettled transactions before gateway settlement. Voids are always for the full amount — no partial voids. If the transaction has already settled, use a refund instead.\n\n**Required Scopes:**\n- `voids:read` - List and retrieve voids\n- `voids:write` - Create voids","x-displayName":"Voids"},{"name":"Credit Notes","description":"Issue formal accounting corrections for paid or partially paid invoices. A credit note is the accounting document; a refund is the money movement. They are often created together.\n\n**Required Scopes:**\n- `credit_notes:read` - List and retrieve credit notes\n- `credit_notes:write` - Create and void credit notes","x-displayName":"Credit Notes"},{"name":"Disputes","description":"Dispute objects represent a customer's challenge to a charge with their card issuer (a chargeback). When a dispute is opened, you can submit evidence to contest it or accept the dispute. RevKeen tracks the dispute lifecycle and notifies you via webhooks at each stage.\n\n**Required Scopes:**\n- `disputes:read` - List and retrieve disputes\n- `disputes:write` - Submit evidence, accept disputes","x-displayName":"Disputes"},{"name":"Payouts","description":"Payout objects represent funds settled to your bank account from processed payments. Track settlement batches, reconcile amounts, and monitor payout timing across all payment gateways.\n\n**Required Scopes:**\n- `payouts:read` - List and retrieve payouts","x-displayName":"Payouts"},{"name":"Events","description":"Event objects represent something that happened in your account — a payment succeeded, an invoice was created, a subscription was canceled. Events are the source of truth for webhook delivery. Use them to debug webhook issues, replay missed events, or build an audit trail.\n\n**Required Scopes:**\n- `events:read` - List and retrieve events\n- `events:write` - Resend webhook events","x-displayName":"Events"},{"name":"Webhooks","description":"Receive real-time event notifications. Configure endpoints for payment events, invoice updates, and more.\n\n## Signature Verification\n\nAll webhooks include a signature in the `X-Revkeen-Signature` header for verification:\n\n```\nX-Revkeen-Signature: t=1705689600,v1=abc123...\n```\n\n**Node.js Example:**\n```javascript\nconst crypto = require('crypto');\n\nfunction verifyWebhook(payload, signature, secret) {\n  const [timestamp, hash] = signature.split(',').map(s => s.split('=')[1]);\n  const expectedHash = crypto\n    .createHmac('sha256', secret)\n    .update(`${timestamp}.${payload}`)\n    .digest('hex');\n  return crypto.timingSafeEqual(Buffer.from(hash), Buffer.from(expectedHash));\n}\n```\n\n## Event Types\n\nEvents use dot notation: `{resource}.{action}`\n- `payment.succeeded`, `payment.failed`\n- `invoice.created`, `invoice.paid`, `invoice.overdue`\n- `subscription.created`, `subscription.canceled`, `subscription.renewed`\n- `customer.created`, `customer.updated`\n- `checkout.session.completed`, `checkout.session.expired`\n- `usage.threshold.reached`, `usage.period_finalized`, `usage.invoice.created`, `usage.event.ingested`, `usage.event.rejected`, `usage.event.excluded`, `usage.event.quarantined`, `usage.cap.exceeded`\n- `meter.created`, `meter.updated`, `meter.archived`\n- `meter_price.created`, `meter_price.updated`, `meter_price.deactivated`\n\nUse wildcards in endpoint configuration: `invoice.*` or `*` for all events.\n","x-displayName":"Webhooks"},{"name":"Webhook Endpoints","description":"Webhook endpoint objects let you register URLs that RevKeen will notify when events occur in your account — payments, subscription changes, invoice updates, and more. Each endpoint can subscribe to specific event types or use wildcards to receive all events.","x-displayName":"Webhook Endpoints"},{"name":"Webhook Deliveries","description":"Individual webhook delivery attempts. Each record represents one attempt chain to one endpoint for one event, capturing the HTTP status code, error, duration, and retry schedule. Use these to debug a specific failed delivery (for example, a 503 from a staging endpoint) and to queue an immediate retry. This is the grain Stripe and Polar expose for webhook debugging — use `/v2/events/:id/resend` when you want to re-dispatch the event across **all** endpoints instead.","x-displayName":"Webhook Deliveries"},{"name":"Customer Portal","description":"A programmatic customer-portal surface. Mint a short-lived bearer token via `POST /v2/customer-portal/sessions` for a specific customer, then hand that token to the customer's browser or to an app acting on their behalf. Subsequent `/v2/customer-portal/*` calls authenticate as that customer — returning only that customer's own subscriptions and invoices, and letting them self-cancel a subscription.\n\nThis is distinct from the hosted customer portal (cookie-based, rendered by RevKeen). Use the hosted portal when you want a drop-in self-service page; use the programmatic surface here when you're embedding self-service inside your own app with your own UI.\n\nTokens are Polar-shaped: prefix `rkcps_`, 60-minute default TTL, no server-side revocation list in v1 (short TTL and re-mint are the revocation story).","x-displayName":"Customer Portal"},{"name":"Customer Meters","description":"Per-customer usage aggregates, computed live from `usage_events`. Given a customer, return the total consumption per meter the merchant has defined. Use this to power \"current usage\" UI inside embedded portals — Polar exposes the same primitive and it's the shape billing-savvy agents expect.\n\nRevKeen does not maintain a separate `customer_meters` table. Aggregates are derived on demand from `usage_events` grouped by `(customer_id, meter_id)`. This keeps the write path cheap and always up-to-date; there is no batch to fall behind.\n\nThe path uses a composite ID `(customer_id, meter_id)` rather than a synthetic single-segment ID because both components are first-class entities the caller already holds.","x-displayName":"Customer Meters"},{"name":"Terminal Payments","description":"Initiate and manage card-present payments via POS terminal devices. Send sale, refund, and void commands to PAX terminals through the RevKeen connector.\n\n**Required Scopes:**\n- `terminal:read` - List and retrieve terminal payments\n- `terminal:write` - Initiate, cancel, refund, void terminal payments\n\n**MCP Tools:** `terminal_payments_initiate`, `terminal_payments_list`, `terminal_payments_get`, `terminal_payments_cancel`, `terminal_payments_refund`, `terminal_payments_void`","x-displayName":"Terminal Payments"},{"name":"Terminal Devices","description":"List and inspect terminal devices registered for your merchant account. Use this endpoint to discover device IDs before initiating terminal payments.\n\n**Required Scopes:**\n- `terminal:read` - List and retrieve terminal devices\n\n**MCP Tools:** `terminal_devices_list`, `terminal_devices_get`","x-displayName":"Terminal Devices"},{"name":"Payment Attempts","description":"Track payment attempts across all gateways. Analyze retry patterns and failure reasons.","x-displayName":"Payment Attempts"},{"name":"External Sync","description":"Sync data with external systems (PracticeHub, Wodify). Upsert by external ID with timestamp-based conflict resolution.","x-displayName":"External Sync"},{"name":"Integrations","description":"Manage external integrations (PracticeHub, Wodify). List active integrations and update status.","x-displayName":"Integrations"},{"name":"Sync State","description":"Track sync progress for external integrations. Get and update sync state by resource type.","x-displayName":"Sync State"},{"name":"Accounting Integrations","description":"Accounting-led invoice payment requests for connected accounting providers (QuickBooks Online, Xero). Claim or reuse an external invoice payment request and mint a provider-neutral checkout session without creating a RevKeen invoice. Requests are idempotent via `Idempotency-Key`.\n\n**Required Scopes:**\n- `integrations:read` - Retrieve accounting-led invoice payment requests\n- `integrations:write` - Create accounting-led invoice payment requests\n- `checkout:write` - Mint the associated checkout session","x-displayName":"Accounting Integrations"},{"name":"Meters","description":"Create and manage usage meters. Meters define how usage events are aggregated for billing. Attach prices to meters for automated usage-based invoicing.\n\n**Required Scopes:**\n- `prices:read` - List and retrieve meters and prices\n- `prices:write` - Create, update, archive meters and prices\n\n**MCP Tools:** `meters_list`, `meters_get`, `meters_create`, `meters_update`, `meters_create_price`, `meters_list_prices`, `meters_update_price`, `meters_deactivate_price`","x-displayName":"Meters"},{"name":"Invoice Line Items","description":"Invoice line item utilities. Includes usage provenance: trace a usage line back to the billing entries, meter matches, and events that produced it.\n\n**Required Scopes:**\n- `invoices:read`","x-displayName":"Invoice Line Items"},{"name":"Usage Events","description":"Ingest, query, and aggregate usage events. Accepted events are persisted as authoritative billing records before derived analytics are updated. Analytics may lag without affecting invoice correctness.\n\n**Required Scopes:**\n- `usage:read` - Query events, aggregate usage\n- `usage:write` - Ingest events\n\n**MCP Tools:** `usage_events_ingest`, `usage_events_list`, `usage_events_aggregate`, `usage_events_dry_run`","x-displayName":"Usage Events"},{"name":"Usage","description":"Query current-period usage balance with estimated charges per meter. Includes included allowance and overage.\n\n**Required Scopes:**\n- `usage:read` - Get usage balance\n\n**MCP Tools:** `usage_balance_get`","x-displayName":"Usage Balance"},{"name":"Analytics","description":"Revenue analytics including MRR summaries, time-series data, accounts receivable aging, days sales outstanding (DSO), and customer lifetime value (LTV). Dashboard-only: these endpoints are not available to API keys.\n\n**Required Scopes:**\n- `analytics:read` - Access analytics endpoints","x-displayName":"Analytics"},{"name":"Finance","description":"Financial summaries and income reports. Get a high-level overview of your business finances including revenue, expenses, and net income.\n\n**Required Scopes:**\n- `finance:read` - Access finance endpoints","x-displayName":"Finance"},{"name":"Transactions","description":"Transactions represent all financial movements in the unified transaction model. This includes sales, refunds, voids, captures, disputes, and adjustments. Every transaction is linked to an invoice and child transactions reference their parent.\n\n**Required Scopes:**\n- `payments:read` - List and retrieve transactions","x-displayName":"Transactions"},{"name":"Imports","description":"Bulk data import jobs. Upload a CSV file to create customers, products, or invoices in bulk. Imports are processed asynchronously — poll `GET /v2/imports/:id` for progress and error details.\n\n**Required Scopes:**\n- `imports:read` - Check import status\n- `imports:write` - Create import jobs","x-displayName":"Imports"},{"name":"Exports","description":"Bulk data export jobs. Generate CSV or XLSX files of customers, invoices, transactions, subscriptions, products, or payment methods. Exports are processed asynchronously — poll `GET /v2/exports/:id` for status and download URL.\n\n**Required Scopes:**\n- `exports:read` - Check export status\n- `exports:write` - Create export jobs","x-displayName":"Exports"}],"paths":{"/invoices/{id}/usage-explain":{"get":{"operationId":"invoice_usage_explain","tags":["Invoices"],"x-fern-sdk-group-name":["invoices"],"x-fern-sdk-method-name":"usageExplain","summary":"Explain invoice usage charges","description":"Return why this invoice billed the amount it did: line-item attribution plus usage provenance counts for usage lines. Use this instead of chaining invoices_get + invoice_line_items_list_usage_events.\n\n---\n\n**Related endpoints**\n\n- `GET /invoices` — List invoices\n- `POST /invoices` — Create invoice\n- `GET /invoices/{id}` — Get invoice\n- `PATCH /invoices/{id}` — Update invoice\n- `GET /invoices/{id}/pdf` — Get invoice PDF link\n- `POST /invoices/{id}/finalize` — Finalize an invoice\n- `POST /invoices/{id}/void` — Void an invoice\n- `POST /invoices/{id}/send` — Send an invoice\n\n**Common errors**\n\n- `401 authentication_error` — missing, invalid, expired, or revoked credential. Codes: `authentication_failed`, `invalid_api_key`, `expired_api_key`, `api_key_revoked`, `session_invalid`, `merchant_required`. Carries a `WWW-Authenticate: Bearer` challenge.\n- `404 resource_missing` — the referenced resource does not exist or is not visible to your key.","parameters":[{"schema":{"type":"string","format":"uuid","description":"Invoice UUID"},"required":true,"description":"Invoice UUID","name":"id","in":"path"}],"responses":{"200":{"description":"Invoice usage explanation","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/InvoiceUsageExplainResponse"}},"required":["data"]}}}},"401":{"description":"Unauthorized","headers":{"WWW-Authenticate":{"description":"RFC 6750 Bearer authentication challenge","schema":{"type":"string"},"example":"Bearer realm=\"revkeen-api\""}}},"404":{"description":"Invoice not found"},"429":{"description":"Too Many Requests - Rate limit exceeded","headers":{"Retry-After":{"description":"Whole seconds until the caller may retry","schema":{"type":"string"},"example":"60"},"X-RateLimit-Limit":{"description":"Request quota for the current window","schema":{"type":"string"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window","schema":{"type":"string"}},"X-RateLimit-Reset":{"description":"UNIX timestamp in seconds when the window resets","schema":{"type":"string"}}}}},"x-cli-group":"invoices","x-cli-name":"invoice-usage-explain","x-codeSamples":[{"lang":"curl","label":"cURL","source":"curl -X GET \"https://api.revkeen.com/v2/invoices/11111111-1111-4111-8111-111111111111/usage-explain\" \\\n  -H \"x-api-key: $REVKEEN_API_KEY\""},{"lang":"typescript","label":"TypeScript (HTTP)","source":"const credential = process.env.REVKEEN_API_KEY;\nif (!credential) throw new Error(\"Set REVKEEN_API_KEY\");\nconst response = await fetch(\"https://api.revkeen.com/v2/invoices/11111111-1111-4111-8111-111111111111/usage-explain\", {\n  method: \"GET\",\n  headers: {\n    \"x-api-key\": credential,\n  },\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"go","label":"Go (HTTP)","source":"package main\nimport (\"fmt\"; \"io\"; \"net/http\"; \"os\"; \"strings\"; \"time\")\nfunc main() {\n  req, err := http.NewRequest(\"GET\", \"https://api.revkeen.com/v2/invoices/11111111-1111-4111-8111-111111111111/usage-explain\", strings.NewReader(\"\"))\n  if err != nil { panic(err) }\n  req.Header.Set(\"x-api-key\", os.Getenv(\"REVKEEN_API_KEY\"))\n  req.Header.Set(\"Content-Type\", \"application/json\")\n  client := &http.Client{Timeout: 10 * time.Second}\n  response, err := client.Do(req)\n  if err != nil { panic(err) }\n  defer response.Body.Close()\n  if response.StatusCode >= 400 { panic(fmt.Sprintf(\"RevKeen request failed (%d)\", response.StatusCode)) }\n  data, err := io.ReadAll(response.Body)\n  if err != nil { panic(err) }\n  fmt.Println(string(data))\n}"},{"lang":"php","label":"PHP (HTTP)","source":"<?php\n$request = curl_init(\"https://api.revkeen.com/v2/invoices/11111111-1111-4111-8111-111111111111/usage-explain\");\ncurl_setopt_array($request, [\n  CURLOPT_CUSTOMREQUEST => \"GET\",\n  CURLOPT_RETURNTRANSFER => true,\n  CURLOPT_TIMEOUT => 10,\n  CURLOPT_HTTPHEADER => [\"x-api-key: \" . getenv(\"REVKEEN_API_KEY\"), \"Content-Type: application/json\"],\n]);\n$response = curl_exec($request);\nif ($response === false) throw new RuntimeException(curl_error($request));\n$status = curl_getinfo($request, CURLINFO_HTTP_CODE);\ncurl_close($request);\nif ($status >= 400) throw new RuntimeException(\"RevKeen request failed ($status)\");\necho $response;"},{"lang":"python","label":"Python","source":"import os\n\nimport requests\n\nresponse = requests.get(\n    \"https://api.revkeen.com/v2/invoices/11111111-1111-4111-8111-111111111111/usage-explain\",\n    headers={\"x-api-key\": os.environ[\"REVKEEN_API_KEY\"]},\n)\n\nresponse.raise_for_status()\nprint(response.json())"},{"lang":"javascript","label":"JavaScript","source":"const credential = process.env.REVKEEN_API_KEY;\nif (!credential) throw new Error(\"Set REVKEEN_API_KEY\");\nconst response = await fetch(\"https://api.revkeen.com/v2/invoices/11111111-1111-4111-8111-111111111111/usage-explain\", {\n  method: \"GET\",\n  headers: {\n    \"x-api-key\": credential,\n  },\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"java","label":"Java","source":"import java.net.URI;\nimport java.net.http.HttpClient;\nimport java.net.http.HttpRequest;\nimport java.net.http.HttpResponse;\n\nHttpClient client = HttpClient.newHttpClient();\n\nHttpRequest request = HttpRequest.newBuilder()\n    .uri(URI.create(\"https://api.revkeen.com/v2/invoices/11111111-1111-4111-8111-111111111111/usage-explain\"))\n    .header(\"x-api-key\", System.getenv(\"REVKEEN_API_KEY\"))\n    .GET()\n    .build();\n\nHttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());\nSystem.out.println(response.body());"},{"lang":"ruby","label":"Ruby","source":"require \"net/http\"\nrequire \"json\"\nrequire \"uri\"\n\nuri = URI(\"https://api.revkeen.com/v2/invoices/11111111-1111-4111-8111-111111111111/usage-explain\")\nhttp = Net::HTTP.new(uri.host, uri.port)\nhttp.use_ssl = true\n\nrequest = Net::HTTP::Get.new(uri)\nrequest[\"x-api-key\"] = ENV[\"REVKEEN_API_KEY\"]\n\nresponse = http.request(request)\nputs response.body"}],"security":[{"apiKey":["invoices:read"]},{"oauth2":["invoices:read"]}]}},"/invoices":{"get":{"operationId":"invoices_list","tags":["Invoices"],"x-fern-sdk-group-name":["invoices"],"summary":"List invoices","description":"List invoices with pagination and filtering.\n\n---\n\n**Related endpoints**\n\n- `GET /invoices/{id}/usage-explain` — Explain invoice usage charges\n- `POST /invoices` — Create invoice\n- `GET /invoices/{id}` — Get invoice\n- `PATCH /invoices/{id}` — Update invoice\n- `GET /invoices/{id}/pdf` — Get invoice PDF link\n- `POST /invoices/{id}/finalize` — Finalize an invoice\n- `POST /invoices/{id}/void` — Void an invoice\n- `POST /invoices/{id}/send` — Send an invoice\n\n**Common errors**\n\n- `400 invalid_request` — malformed payload or failed validation.\n\n**Pagination**\n\nOffset-based with `limit` (default 25, max 100) and `offset`. The response `pagination` block includes `total` and `hasMore`. See [the pagination guide](/docs/fundamentals/pagination) for SDK auto-paging helpers.","parameters":[{"schema":{"type":"string","description":"Filter by invoice status"},"required":false,"description":"Filter by invoice status","name":"status","in":"query"},{"schema":{"type":"string","format":"uuid","description":"Filter by customer ID (UUID)"},"required":false,"description":"Filter by customer ID (UUID)","name":"customerId","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":100,"default":20,"description":"Maximum number of results (1-100)"},"required":false,"description":"Maximum number of results (1-100)","name":"limit","in":"query"},{"schema":{"type":["integer","null"],"minimum":0,"default":0,"description":"Number of results to skip"},"required":false,"description":"Number of results to skip","name":"offset","in":"query"}],"responses":{"200":{"description":"List of invoices","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InvoiceListResponse"}}}},"400":{"description":"A query parameter failed validation; `param` names it","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InvoiceListValidationError"}}}},"401":{"description":"Unauthorized - Invalid or missing API key","headers":{"WWW-Authenticate":{"description":"RFC 6750 Bearer authentication challenge","schema":{"type":"string"},"example":"Bearer realm=\"revkeen-api\""}}},"429":{"description":"Too Many Requests - Rate limit exceeded","headers":{"Retry-After":{"description":"Whole seconds until the caller may retry","schema":{"type":"string"},"example":"60"},"X-RateLimit-Limit":{"description":"Request quota for the current window","schema":{"type":"string"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window","schema":{"type":"string"}},"X-RateLimit-Reset":{"description":"UNIX timestamp in seconds when the window resets","schema":{"type":"string"}}}}},"x-cli-group":"invoices","x-cli-name":"list","x-codeSamples":[{"lang":"curl","label":"cURL","source":"curl -X GET \"https://api.revkeen.com/v2/invoices?status=example&customerId=11111111-1111-4111-8111-111111111111\" \\\n  -H \"x-api-key: $REVKEEN_API_KEY\""},{"lang":"typescript","label":"TypeScript (SDK)","source":"// npm install @revkeen/sdk@1.20260822.1541\nimport { RevKeenClient } from \"@revkeen/sdk\";\nconst apiKey = process.env.REVKEEN_API_KEY;\nif (!apiKey) throw new Error(\"Set REVKEEN_API_KEY\");\nconst client = new RevKeenClient({ apiKey, baseUrl: \"https://api.revkeen.com/v2\" });\nconst result = await client.invoices.list({\n  \"status\": \"example\",\n  \"customerId\": \"11111111-1111-4111-8111-111111111111\"\n});\nconsole.log(result);"},{"lang":"go","label":"Go (HTTP)","source":"package main\nimport (\"fmt\"; \"io\"; \"net/http\"; \"os\"; \"strings\"; \"time\")\nfunc main() {\n  req, err := http.NewRequest(\"GET\", \"https://api.revkeen.com/v2/invoices?status=example&customerId=11111111-1111-4111-8111-111111111111\", strings.NewReader(\"\"))\n  if err != nil { panic(err) }\n  req.Header.Set(\"x-api-key\", os.Getenv(\"REVKEEN_API_KEY\"))\n  req.Header.Set(\"Content-Type\", \"application/json\")\n  client := &http.Client{Timeout: 10 * time.Second}\n  response, err := client.Do(req)\n  if err != nil { panic(err) }\n  defer response.Body.Close()\n  if response.StatusCode >= 400 { panic(fmt.Sprintf(\"RevKeen request failed (%d)\", response.StatusCode)) }\n  data, err := io.ReadAll(response.Body)\n  if err != nil { panic(err) }\n  fmt.Println(string(data))\n}"},{"lang":"php","label":"PHP (HTTP)","source":"<?php\n$request = curl_init(\"https://api.revkeen.com/v2/invoices?status=example&customerId=11111111-1111-4111-8111-111111111111\");\ncurl_setopt_array($request, [\n  CURLOPT_CUSTOMREQUEST => \"GET\",\n  CURLOPT_RETURNTRANSFER => true,\n  CURLOPT_TIMEOUT => 10,\n  CURLOPT_HTTPHEADER => [\"x-api-key: \" . getenv(\"REVKEEN_API_KEY\"), \"Content-Type: application/json\"],\n]);\n$response = curl_exec($request);\nif ($response === false) throw new RuntimeException(curl_error($request));\n$status = curl_getinfo($request, CURLINFO_HTTP_CODE);\ncurl_close($request);\nif ($status >= 400) throw new RuntimeException(\"RevKeen request failed ($status)\");\necho $response;"},{"lang":"python","label":"Python","source":"import os\n\nimport requests\n\nresponse = requests.get(\n    \"https://api.revkeen.com/v2/invoices\",\n    headers={\"x-api-key\": os.environ[\"REVKEEN_API_KEY\"]},\n    params={\n        \"status\": \"example\",\n        \"customerId\": \"11111111-1111-4111-8111-111111111111\",\n    },\n)\n\nresponse.raise_for_status()\nprint(response.json())"},{"lang":"javascript","label":"JavaScript","source":"const credential = process.env.REVKEEN_API_KEY;\nif (!credential) throw new Error(\"Set REVKEEN_API_KEY\");\nconst response = await fetch(\"https://api.revkeen.com/v2/invoices?status=example&customerId=11111111-1111-4111-8111-111111111111\", {\n  method: \"GET\",\n  headers: {\n    \"x-api-key\": credential,\n  },\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"java","label":"Java","source":"import java.net.URI;\nimport java.net.http.HttpClient;\nimport java.net.http.HttpRequest;\nimport java.net.http.HttpResponse;\n\nHttpClient client = HttpClient.newHttpClient();\n\nHttpRequest request = HttpRequest.newBuilder()\n    .uri(URI.create(\"https://api.revkeen.com/v2/invoices?status=example&customerId=11111111-1111-4111-8111-111111111111\"))\n    .header(\"x-api-key\", System.getenv(\"REVKEEN_API_KEY\"))\n    .GET()\n    .build();\n\nHttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());\nSystem.out.println(response.body());"},{"lang":"ruby","label":"Ruby","source":"require \"net/http\"\nrequire \"json\"\nrequire \"uri\"\n\nuri = URI(\"https://api.revkeen.com/v2/invoices?status=example&customerId=11111111-1111-4111-8111-111111111111\")\nhttp = Net::HTTP.new(uri.host, uri.port)\nhttp.use_ssl = true\n\nrequest = Net::HTTP::Get.new(uri)\nrequest[\"x-api-key\"] = ENV[\"REVKEEN_API_KEY\"]\n\nresponse = http.request(request)\nputs response.body"}],"security":[{"apiKey":["invoices:read"]},{"oauth2":["invoices:read"]}]},"post":{"operationId":"invoices_create","tags":["Invoices"],"x-fern-sdk-group-name":["invoices"],"summary":"Create invoice","description":"Create a new invoice.\n\n---\n\n**Related endpoints**\n\n- `GET /invoices/{id}/usage-explain` — Explain invoice usage charges\n- `GET /invoices` — List invoices\n- `GET /invoices/{id}` — Get invoice\n- `PATCH /invoices/{id}` — Update invoice\n- `GET /invoices/{id}/pdf` — Get invoice PDF link\n- `POST /invoices/{id}/finalize` — Finalize an invoice\n- `POST /invoices/{id}/void` — Void an invoice\n- `POST /invoices/{id}/send` — Send an invoice\n\n**Idempotency**\n\nPass an `Idempotency-Key` header (UUID v4 recommended) to make retries safe. Keys are valid for 24 hours; see [the idempotency guide](/docs/fundamentals/idempotency).","requestBody":{"description":"Invoice creation details","required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"customer_uuid":{"type":"string","format":"uuid"},"invoice_number":{"type":"string"},"total_minor":{"type":"integer"},"currency":{"type":"string","pattern":"^[A-Za-z]{3}$"},"due_date":{"type":"string"},"custom_fields":{"type":"object","additionalProperties":true,"description":"Key-value pairs for custom fields"},"subscription_terms":{"$ref":"#/components/schemas/SubscriptionTerms"},"allowed_methods":{"type":"array","items":{"type":"string","enum":["card","direct_debit","in_store"]},"example":["card","direct_debit"],"description":"Restrict checkout payment methods for this invoice. When set, narrows available rails for checkout sessions created against this invoice. Omit to use merchant default."}},"required":["customer_uuid","total_minor","currency"],"additionalProperties":false}}}},"responses":{"201":{"description":"Invoice created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InvoiceResponse"}}}},"401":{"description":"Unauthorized - Invalid or missing API key","headers":{"WWW-Authenticate":{"description":"RFC 6750 Bearer authentication challenge","schema":{"type":"string"},"example":"Bearer realm=\"revkeen-api\""}}},"429":{"description":"Too Many Requests - Rate limit exceeded","headers":{"Retry-After":{"description":"Whole seconds until the caller may retry","schema":{"type":"string"},"example":"60"},"X-RateLimit-Limit":{"description":"Request quota for the current window","schema":{"type":"string"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window","schema":{"type":"string"}},"X-RateLimit-Reset":{"description":"UNIX timestamp in seconds when the window resets","schema":{"type":"string"}}}}},"x-cli-group":"invoices","x-cli-name":"create","x-codeSamples":[{"lang":"curl","label":"cURL","source":"curl -X POST \"https://api.revkeen.com/v2/invoices\" \\\n  -H \"x-api-key: $REVKEEN_API_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"customer_uuid\": \"11111111-1111-4111-8111-111111111111\",\n    \"total_minor\": 1,\n    \"currency\": \"USD\"\n  }'"},{"lang":"typescript","label":"TypeScript (SDK)","source":"// npm install @revkeen/sdk@1.20260822.1541\nimport { RevKeenClient } from \"@revkeen/sdk\";\nconst apiKey = process.env.REVKEEN_API_KEY;\nif (!apiKey) throw new Error(\"Set REVKEEN_API_KEY\");\nconst client = new RevKeenClient({ apiKey, baseUrl: \"https://api.revkeen.com/v2\" });\nconst result = await client.invoices.create({\n  \"customer_uuid\": \"11111111-1111-4111-8111-111111111111\",\n  \"total_minor\": 1,\n  \"currency\": \"USD\"\n});\nconsole.log(result);"},{"lang":"go","label":"Go (HTTP)","source":"package main\nimport (\"fmt\"; \"io\"; \"net/http\"; \"os\"; \"strings\"; \"time\")\nfunc main() {\n  req, err := http.NewRequest(\"POST\", \"https://api.revkeen.com/v2/invoices\", strings.NewReader(\"{\\\"customer_uuid\\\":\\\"11111111-1111-4111-8111-111111111111\\\",\\\"total_minor\\\":1,\\\"currency\\\":\\\"USD\\\"}\"))\n  if err != nil { panic(err) }\n  req.Header.Set(\"x-api-key\", os.Getenv(\"REVKEEN_API_KEY\"))\n  req.Header.Set(\"Content-Type\", \"application/json\")\n  client := &http.Client{Timeout: 10 * time.Second}\n  response, err := client.Do(req)\n  if err != nil { panic(err) }\n  defer response.Body.Close()\n  if response.StatusCode >= 400 { panic(fmt.Sprintf(\"RevKeen request failed (%d)\", response.StatusCode)) }\n  data, err := io.ReadAll(response.Body)\n  if err != nil { panic(err) }\n  fmt.Println(string(data))\n}"},{"lang":"php","label":"PHP (HTTP)","source":"<?php\n$request = curl_init(\"https://api.revkeen.com/v2/invoices\");\ncurl_setopt_array($request, [\n  CURLOPT_CUSTOMREQUEST => \"POST\",\n  CURLOPT_RETURNTRANSFER => true,\n  CURLOPT_TIMEOUT => 10,\n  CURLOPT_HTTPHEADER => [\"x-api-key: \" . getenv(\"REVKEEN_API_KEY\"), \"Content-Type: application/json\"],\n  CURLOPT_POSTFIELDS => '{\\\"customer_uuid\\\":\\\"11111111-1111-4111-8111-111111111111\\\",\\\"total_minor\\\":1,\\\"currency\\\":\\\"USD\\\"}',\n]);\n$response = curl_exec($request);\nif ($response === false) throw new RuntimeException(curl_error($request));\n$status = curl_getinfo($request, CURLINFO_HTTP_CODE);\ncurl_close($request);\nif ($status >= 400) throw new RuntimeException(\"RevKeen request failed ($status)\");\necho $response;"},{"lang":"python","label":"Python","source":"import os\n\nimport requests\n\nresponse = requests.post(\n    \"https://api.revkeen.com/v2/invoices\",\n    headers={\"x-api-key\": os.environ[\"REVKEEN_API_KEY\"]},\n    json={\n        \"customer_uuid\": \"11111111-1111-4111-8111-111111111111\",\n        \"total_minor\": 1,\n        \"currency\": \"USD\",\n    },\n)\n\nresponse.raise_for_status()\nprint(response.json())"},{"lang":"javascript","label":"JavaScript","source":"const credential = process.env.REVKEEN_API_KEY;\nif (!credential) throw new Error(\"Set REVKEEN_API_KEY\");\nconst response = await fetch(\"https://api.revkeen.com/v2/invoices\", {\n  method: \"POST\",\n  headers: {\n    \"x-api-key\": credential,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n    \"customer_uuid\": \"11111111-1111-4111-8111-111111111111\",\n    \"total_minor\": 1,\n    \"currency\": \"USD\"\n  }),\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"java","label":"Java","source":"import java.net.URI;\nimport java.net.http.HttpClient;\nimport java.net.http.HttpRequest;\nimport java.net.http.HttpResponse;\n\nHttpClient client = HttpClient.newHttpClient();\n\nHttpRequest request = HttpRequest.newBuilder()\n    .uri(URI.create(\"https://api.revkeen.com/v2/invoices\"))\n    .header(\"x-api-key\", System.getenv(\"REVKEEN_API_KEY\"))\n    .header(\"Content-Type\", \"application/json\")\n    .method(\"POST\", HttpRequest.BodyPublishers.ofString(\"\"\"\n        {\n          \"customer_uuid\": \"11111111-1111-4111-8111-111111111111\",\n          \"total_minor\": 1,\n          \"currency\": \"USD\"\n        }\n        \"\"\"))\n    .build();\n\nHttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());\nSystem.out.println(response.body());"},{"lang":"ruby","label":"Ruby","source":"require \"net/http\"\nrequire \"json\"\nrequire \"uri\"\n\nuri = URI(\"https://api.revkeen.com/v2/invoices\")\nhttp = Net::HTTP.new(uri.host, uri.port)\nhttp.use_ssl = true\n\nrequest = Net::HTTP::Post.new(uri)\nrequest[\"x-api-key\"] = ENV[\"REVKEEN_API_KEY\"]\nrequest[\"Content-Type\"] = \"application/json\"\nrequest.body = {\n  \"customer_uuid\" => \"11111111-1111-4111-8111-111111111111\",\n  \"total_minor\" => 1,\n  \"currency\" => \"USD\",\n}.to_json\n\nresponse = http.request(request)\nputs response.body"}],"security":[{"apiKey":["invoices:write"]},{"oauth2":["invoices:write"]}]}},"/invoices/{id}":{"get":{"operationId":"invoices_get","tags":["Invoices"],"x-fern-sdk-group-name":["invoices"],"summary":"Get invoice","description":"Get a single invoice by ID.\n\n---\n\n**Related endpoints**\n\n- `GET /invoices/{id}/usage-explain` — Explain invoice usage charges\n- `GET /invoices` — List invoices\n- `POST /invoices` — Create invoice\n- `PATCH /invoices/{id}` — Update invoice\n- `GET /invoices/{id}/pdf` — Get invoice PDF link\n- `POST /invoices/{id}/finalize` — Finalize an invoice\n- `POST /invoices/{id}/void` — Void an invoice\n- `POST /invoices/{id}/send` — Send an invoice","parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Invoice details","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InvoiceResponse"}}}},"401":{"description":"Unauthorized - Invalid or missing API key","headers":{"WWW-Authenticate":{"description":"RFC 6750 Bearer authentication challenge","schema":{"type":"string"},"example":"Bearer realm=\"revkeen-api\""}}},"429":{"description":"Too Many Requests - Rate limit exceeded","headers":{"Retry-After":{"description":"Whole seconds until the caller may retry","schema":{"type":"string"},"example":"60"},"X-RateLimit-Limit":{"description":"Request quota for the current window","schema":{"type":"string"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window","schema":{"type":"string"}},"X-RateLimit-Reset":{"description":"UNIX timestamp in seconds when the window resets","schema":{"type":"string"}}}}},"x-cli-group":"invoices","x-cli-name":"get","x-codeSamples":[{"lang":"curl","label":"cURL","source":"curl -X GET \"https://api.revkeen.com/v2/invoices/11111111-1111-4111-8111-111111111111\" \\\n  -H \"x-api-key: $REVKEEN_API_KEY\""},{"lang":"typescript","label":"TypeScript (SDK)","source":"// npm install @revkeen/sdk@1.20260822.1541\nimport { RevKeenClient } from \"@revkeen/sdk\";\nconst apiKey = process.env.REVKEEN_API_KEY;\nif (!apiKey) throw new Error(\"Set REVKEEN_API_KEY\");\nconst client = new RevKeenClient({ apiKey, baseUrl: \"https://api.revkeen.com/v2\" });\nconst result = await client.invoices.get(\"11111111-1111-4111-8111-111111111111\");\nconsole.log(result);"},{"lang":"go","label":"Go (HTTP)","source":"package main\nimport (\"fmt\"; \"io\"; \"net/http\"; \"os\"; \"strings\"; \"time\")\nfunc main() {\n  req, err := http.NewRequest(\"GET\", \"https://api.revkeen.com/v2/invoices/11111111-1111-4111-8111-111111111111\", strings.NewReader(\"\"))\n  if err != nil { panic(err) }\n  req.Header.Set(\"x-api-key\", os.Getenv(\"REVKEEN_API_KEY\"))\n  req.Header.Set(\"Content-Type\", \"application/json\")\n  client := &http.Client{Timeout: 10 * time.Second}\n  response, err := client.Do(req)\n  if err != nil { panic(err) }\n  defer response.Body.Close()\n  if response.StatusCode >= 400 { panic(fmt.Sprintf(\"RevKeen request failed (%d)\", response.StatusCode)) }\n  data, err := io.ReadAll(response.Body)\n  if err != nil { panic(err) }\n  fmt.Println(string(data))\n}"},{"lang":"php","label":"PHP (HTTP)","source":"<?php\n$request = curl_init(\"https://api.revkeen.com/v2/invoices/11111111-1111-4111-8111-111111111111\");\ncurl_setopt_array($request, [\n  CURLOPT_CUSTOMREQUEST => \"GET\",\n  CURLOPT_RETURNTRANSFER => true,\n  CURLOPT_TIMEOUT => 10,\n  CURLOPT_HTTPHEADER => [\"x-api-key: \" . getenv(\"REVKEEN_API_KEY\"), \"Content-Type: application/json\"],\n]);\n$response = curl_exec($request);\nif ($response === false) throw new RuntimeException(curl_error($request));\n$status = curl_getinfo($request, CURLINFO_HTTP_CODE);\ncurl_close($request);\nif ($status >= 400) throw new RuntimeException(\"RevKeen request failed ($status)\");\necho $response;"},{"lang":"python","label":"Python","source":"import os\n\nimport requests\n\nresponse = requests.get(\n    \"https://api.revkeen.com/v2/invoices/11111111-1111-4111-8111-111111111111\",\n    headers={\"x-api-key\": os.environ[\"REVKEEN_API_KEY\"]},\n)\n\nresponse.raise_for_status()\nprint(response.json())"},{"lang":"javascript","label":"JavaScript","source":"const credential = process.env.REVKEEN_API_KEY;\nif (!credential) throw new Error(\"Set REVKEEN_API_KEY\");\nconst response = await fetch(\"https://api.revkeen.com/v2/invoices/11111111-1111-4111-8111-111111111111\", {\n  method: \"GET\",\n  headers: {\n    \"x-api-key\": credential,\n  },\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"java","label":"Java","source":"import java.net.URI;\nimport java.net.http.HttpClient;\nimport java.net.http.HttpRequest;\nimport java.net.http.HttpResponse;\n\nHttpClient client = HttpClient.newHttpClient();\n\nHttpRequest request = HttpRequest.newBuilder()\n    .uri(URI.create(\"https://api.revkeen.com/v2/invoices/11111111-1111-4111-8111-111111111111\"))\n    .header(\"x-api-key\", System.getenv(\"REVKEEN_API_KEY\"))\n    .GET()\n    .build();\n\nHttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());\nSystem.out.println(response.body());"},{"lang":"ruby","label":"Ruby","source":"require \"net/http\"\nrequire \"json\"\nrequire \"uri\"\n\nuri = URI(\"https://api.revkeen.com/v2/invoices/11111111-1111-4111-8111-111111111111\")\nhttp = Net::HTTP.new(uri.host, uri.port)\nhttp.use_ssl = true\n\nrequest = Net::HTTP::Get.new(uri)\nrequest[\"x-api-key\"] = ENV[\"REVKEEN_API_KEY\"]\n\nresponse = http.request(request)\nputs response.body"}],"security":[{"apiKey":["invoices:read"]},{"oauth2":["invoices:read"]}]},"patch":{"operationId":"invoices_update","tags":["Invoices"],"x-fern-sdk-group-name":["invoices"],"summary":"Update invoice","description":"Update an existing invoice.\n\n---\n\n**Related endpoints**\n\n- `GET /invoices/{id}/usage-explain` — Explain invoice usage charges\n- `GET /invoices` — List invoices\n- `POST /invoices` — Create invoice\n- `GET /invoices/{id}` — Get invoice\n- `GET /invoices/{id}/pdf` — Get invoice PDF link\n- `POST /invoices/{id}/finalize` — Finalize an invoice\n- `POST /invoices/{id}/void` — Void an invoice\n- `POST /invoices/{id}/send` — Send an invoice\n\n**Common errors**\n\n- `400 invalid_request` — malformed payload or failed validation.\n\n**Idempotency**\n\nPass an `Idempotency-Key` header (UUID v4 recommended) to make retries safe. Keys are valid for 24 hours; see [the idempotency guide](/docs/fundamentals/idempotency).","parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"}],"requestBody":{"description":"Invoice update details","required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"total_minor":{"type":"integer","description":"Header total in minor units. Accepted only on a draft invoice with no line items; amount_due_minor follows it."},"due_date":{"type":"string"},"allowed_methods":{"type":"array","items":{"type":"string","enum":["card","direct_debit","in_store"]},"example":["card","direct_debit"],"description":"Update the invoice's restricted payment methods. Empty array clears restriction (merchant defaults apply); omit to leave unchanged."},"subscription_terms":{"type":["object","null"],"properties":{"collection_method":{"type":"string","enum":["charge_automatically","send_invoice"],"description":"How to collect payment for recurring invoices","example":"charge_automatically"},"start_mode":{"type":"string","enum":["when_paid","on_invoice_date","first_of_next_month","specific_date"],"description":"When the subscription billing cycle starts","example":"when_paid"},"start_date":{"type":"string","description":"Start date (ISO 8601) — required when start_mode is 'specific_date'"},"duration_type":{"type":"string","enum":["until_cancelled","fixed_cycles","end_date"],"description":"How the subscription duration is determined","example":"until_cancelled"},"duration_count":{"type":"integer","minimum":1,"description":"Number of billing cycles — required when duration_type is 'fixed_cycles'","example":12},"end_date":{"type":"string","description":"End date (ISO 8601) — required when duration_type is 'end_date'"},"first_payment_behavior":{"type":"string","enum":["charge_first_cycle_now","defer_first_cycle_to_start_date"],"description":"Whether to charge the first cycle immediately or defer to the start date","example":"charge_first_cycle_now"}},"required":["collection_method","start_mode","duration_type","first_payment_behavior"],"description":"Subscription billing terms — set to null to remove"}},"additionalProperties":false}}}},"responses":{"200":{"description":"Invoice updated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InvoiceResponse"}}}},"400":{"description":"Bad Request - a field failed validation, an unknown field was sent (`status` is not accepted: an invoice is finalized, sent, voided or paid through its actions), or `total_minor` was sent for an invoice that is not a draft without line items","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationError"}}}},"401":{"description":"Unauthorized - Invalid or missing API key","headers":{"WWW-Authenticate":{"description":"RFC 6750 Bearer authentication challenge","schema":{"type":"string"},"example":"Bearer realm=\"revkeen-api\""}}},"429":{"description":"Too Many Requests - Rate limit exceeded","headers":{"Retry-After":{"description":"Whole seconds until the caller may retry","schema":{"type":"string"},"example":"60"},"X-RateLimit-Limit":{"description":"Request quota for the current window","schema":{"type":"string"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window","schema":{"type":"string"}},"X-RateLimit-Reset":{"description":"UNIX timestamp in seconds when the window resets","schema":{"type":"string"}}}}},"x-cli-group":"invoices","x-cli-name":"update","x-codeSamples":[{"lang":"curl","label":"cURL","source":"curl -X PATCH \"https://api.revkeen.com/v2/invoices/11111111-1111-4111-8111-111111111111\" \\\n  -H \"x-api-key: $REVKEEN_API_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"total_minor\": 1,\n    \"due_date\": \"example\",\n    \"allowed_methods\": [\n      \"card\",\n      \"direct_debit\"\n    ],\n    \"subscription_terms\": {\n      \"collection_method\": \"charge_automatically\",\n      \"start_mode\": \"when_paid\",\n      \"duration_type\": \"until_cancelled\",\n      \"first_payment_behavior\": \"charge_first_cycle_now\"\n    }\n  }'"},{"lang":"typescript","label":"TypeScript (SDK)","source":"// npm install @revkeen/sdk@1.20260822.1541\nimport { RevKeenClient } from \"@revkeen/sdk\";\nconst apiKey = process.env.REVKEEN_API_KEY;\nif (!apiKey) throw new Error(\"Set REVKEEN_API_KEY\");\nconst client = new RevKeenClient({ apiKey, baseUrl: \"https://api.revkeen.com/v2\" });\nconst result = await client.invoices.update(\"11111111-1111-4111-8111-111111111111\", {\n  \"total_minor\": 1,\n  \"due_date\": \"example\",\n  \"allowed_methods\": [\n    \"card\",\n    \"direct_debit\"\n  ],\n  \"subscription_terms\": {\n    \"collection_method\": \"charge_automatically\",\n    \"start_mode\": \"when_paid\",\n    \"duration_type\": \"until_cancelled\",\n    \"first_payment_behavior\": \"charge_first_cycle_now\"\n  }\n});\nconsole.log(result);"},{"lang":"go","label":"Go (HTTP)","source":"package main\nimport (\"fmt\"; \"io\"; \"net/http\"; \"os\"; \"strings\"; \"time\")\nfunc main() {\n  req, err := http.NewRequest(\"PATCH\", \"https://api.revkeen.com/v2/invoices/11111111-1111-4111-8111-111111111111\", strings.NewReader(\"{\\\"total_minor\\\":1,\\\"due_date\\\":\\\"example\\\",\\\"allowed_methods\\\":[\\\"card\\\",\\\"direct_debit\\\"],\\\"subscription_terms\\\":{\\\"collection_method\\\":\\\"charge_automatically\\\",\\\"start_mode\\\":\\\"when_paid\\\",\\\"duration_type\\\":\\\"until_cancelled\\\",\\\"first_payment_behavior\\\":\\\"charge_first_cycle_now\\\"}}\"))\n  if err != nil { panic(err) }\n  req.Header.Set(\"x-api-key\", os.Getenv(\"REVKEEN_API_KEY\"))\n  req.Header.Set(\"Content-Type\", \"application/json\")\n  client := &http.Client{Timeout: 10 * time.Second}\n  response, err := client.Do(req)\n  if err != nil { panic(err) }\n  defer response.Body.Close()\n  if response.StatusCode >= 400 { panic(fmt.Sprintf(\"RevKeen request failed (%d)\", response.StatusCode)) }\n  data, err := io.ReadAll(response.Body)\n  if err != nil { panic(err) }\n  fmt.Println(string(data))\n}"},{"lang":"php","label":"PHP (HTTP)","source":"<?php\n$request = curl_init(\"https://api.revkeen.com/v2/invoices/11111111-1111-4111-8111-111111111111\");\ncurl_setopt_array($request, [\n  CURLOPT_CUSTOMREQUEST => \"PATCH\",\n  CURLOPT_RETURNTRANSFER => true,\n  CURLOPT_TIMEOUT => 10,\n  CURLOPT_HTTPHEADER => [\"x-api-key: \" . getenv(\"REVKEEN_API_KEY\"), \"Content-Type: application/json\"],\n  CURLOPT_POSTFIELDS => '{\\\"total_minor\\\":1,\\\"due_date\\\":\\\"example\\\",\\\"allowed_methods\\\":[\\\"card\\\",\\\"direct_debit\\\"],\\\"subscription_terms\\\":{\\\"collection_method\\\":\\\"charge_automatically\\\",\\\"start_mode\\\":\\\"when_paid\\\",\\\"duration_type\\\":\\\"until_cancelled\\\",\\\"first_payment_behavior\\\":\\\"charge_first_cycle_now\\\"}}',\n]);\n$response = curl_exec($request);\nif ($response === false) throw new RuntimeException(curl_error($request));\n$status = curl_getinfo($request, CURLINFO_HTTP_CODE);\ncurl_close($request);\nif ($status >= 400) throw new RuntimeException(\"RevKeen request failed ($status)\");\necho $response;"},{"lang":"python","label":"Python","source":"import os\n\nimport requests\n\nresponse = requests.patch(\n    \"https://api.revkeen.com/v2/invoices/11111111-1111-4111-8111-111111111111\",\n    headers={\"x-api-key\": os.environ[\"REVKEEN_API_KEY\"]},\n    json={\n        \"total_minor\": 1,\n        \"due_date\": \"example\",\n        \"allowed_methods\": [\n            \"card\",\n            \"direct_debit\",\n        ],\n        \"subscription_terms\": {\n            \"collection_method\": \"charge_automatically\",\n            \"start_mode\": \"when_paid\",\n            \"duration_type\": \"until_cancelled\",\n            \"first_payment_behavior\": \"charge_first_cycle_now\",\n        },\n    },\n)\n\nresponse.raise_for_status()\nprint(response.json())"},{"lang":"javascript","label":"JavaScript","source":"const credential = process.env.REVKEEN_API_KEY;\nif (!credential) throw new Error(\"Set REVKEEN_API_KEY\");\nconst response = await fetch(\"https://api.revkeen.com/v2/invoices/11111111-1111-4111-8111-111111111111\", {\n  method: \"PATCH\",\n  headers: {\n    \"x-api-key\": credential,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n    \"total_minor\": 1,\n    \"due_date\": \"example\",\n    \"allowed_methods\": [\n      \"card\",\n      \"direct_debit\"\n    ],\n    \"subscription_terms\": {\n      \"collection_method\": \"charge_automatically\",\n      \"start_mode\": \"when_paid\",\n      \"duration_type\": \"until_cancelled\",\n      \"first_payment_behavior\": \"charge_first_cycle_now\"\n    }\n  }),\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"java","label":"Java","source":"import java.net.URI;\nimport java.net.http.HttpClient;\nimport java.net.http.HttpRequest;\nimport java.net.http.HttpResponse;\n\nHttpClient client = HttpClient.newHttpClient();\n\nHttpRequest request = HttpRequest.newBuilder()\n    .uri(URI.create(\"https://api.revkeen.com/v2/invoices/11111111-1111-4111-8111-111111111111\"))\n    .header(\"x-api-key\", System.getenv(\"REVKEEN_API_KEY\"))\n    .header(\"Content-Type\", \"application/json\")\n    .method(\"PATCH\", HttpRequest.BodyPublishers.ofString(\"\"\"\n        {\n          \"total_minor\": 1,\n          \"due_date\": \"example\",\n          \"allowed_methods\": [\n            \"card\",\n            \"direct_debit\"\n          ],\n          \"subscription_terms\": {\n            \"collection_method\": \"charge_automatically\",\n            \"start_mode\": \"when_paid\",\n            \"duration_type\": \"until_cancelled\",\n            \"first_payment_behavior\": \"charge_first_cycle_now\"\n          }\n        }\n        \"\"\"))\n    .build();\n\nHttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());\nSystem.out.println(response.body());"},{"lang":"ruby","label":"Ruby","source":"require \"net/http\"\nrequire \"json\"\nrequire \"uri\"\n\nuri = URI(\"https://api.revkeen.com/v2/invoices/11111111-1111-4111-8111-111111111111\")\nhttp = Net::HTTP.new(uri.host, uri.port)\nhttp.use_ssl = true\n\nrequest = Net::HTTP::Patch.new(uri)\nrequest[\"x-api-key\"] = ENV[\"REVKEEN_API_KEY\"]\nrequest[\"Content-Type\"] = \"application/json\"\nrequest.body = {\n  \"total_minor\" => 1,\n  \"due_date\" => \"example\",\n  \"allowed_methods\" => [\n    \"card\",\n    \"direct_debit\",\n  ],\n  \"subscription_terms\" => {\n    \"collection_method\" => \"charge_automatically\",\n    \"start_mode\" => \"when_paid\",\n    \"duration_type\" => \"until_cancelled\",\n    \"first_payment_behavior\" => \"charge_first_cycle_now\",\n  },\n}.to_json\n\nresponse = http.request(request)\nputs response.body"}],"security":[{"apiKey":["invoices:write"]},{"oauth2":["invoices:write"]}]}},"/invoices/{id}/pdf":{"get":{"operationId":"invoices_get_pdf","tags":["Invoices"],"x-fern-sdk-group-name":["invoices"],"summary":"Get invoice PDF link","description":"Returns a short-lived presigned URL (valid for 5 minutes) that downloads the invoice PDF. The response never contains the PDF itself. Only invoices that have a rendered PDF can be downloaded. When there is none yet, the response is 409 invoice_pdf_not_ready. For an invoice that is not a draft, the request also queues a render, so retry shortly. A draft never gets a stored PDF here. Treat the URL as a secret and do not log it.\n\n---\n\n**Related endpoints**\n\n- `GET /invoices/{id}/usage-explain` — Explain invoice usage charges\n- `GET /invoices` — List invoices\n- `POST /invoices` — Create invoice\n- `GET /invoices/{id}` — Get invoice\n- `PATCH /invoices/{id}` — Update invoice\n- `POST /invoices/{id}/finalize` — Finalize an invoice\n- `POST /invoices/{id}/void` — Void an invoice\n- `POST /invoices/{id}/send` — Send an invoice\n\n**Common errors**\n\n- `401 authentication_error` — missing, invalid, expired, or revoked credential. Codes: `authentication_failed`, `invalid_api_key`, `expired_api_key`, `api_key_revoked`, `session_invalid`, `merchant_required`. Carries a `WWW-Authenticate: Bearer` challenge.\n- `403 authorization_error` — the caller is identified but the action is denied. Codes include `insufficient_permissions`, `ip_not_allowed`, `merchant_mismatch`, and `origin_not_allowed`.\n- `404 resource_missing` — the referenced resource does not exist or is not visible to your key.\n- `409 conflict` — Idempotency-Key collision with a different body, or a concurrent state-transition conflict.\n- `429 rate_limit_error` — code `rate_limit_exceeded`. Back off using the `Retry-After` header (whole seconds); `X-RateLimit-Reset` is a UNIX timestamp in seconds.","parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"A short-lived download link for the invoice PDF","headers":{"Cache-Control":{"schema":{"type":"string","description":"Always no-store: the URL is a bearer capability and must not be cached.","example":"no-store"},"required":true,"description":"Always no-store: the URL is a bearer capability and must not be cached."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/InvoicePdfResponse"}}}},"401":{"description":"Unauthorized - Invalid or missing API key","headers":{"WWW-Authenticate":{"schema":{"type":"string","description":"RFC 6750 Bearer authentication challenge","example":"Bearer realm=\"revkeen-api\""},"required":true,"description":"RFC 6750 Bearer authentication challenge"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthenticationError"}}}},"403":{"description":"Forbidden - Insufficient permissions","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ForbiddenError"}}}},"404":{"description":"Not Found - Resource does not exist","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotFoundError"}}}},"409":{"description":"Conflict - the invoice has no rendered PDF yet (invoice_pdf_not_ready)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InvoicePdfNotReadyError"}}}},"429":{"description":"Too Many Requests - Rate limit exceeded","headers":{"Retry-After":{"schema":{"type":"string","description":"Whole seconds until the caller may retry","example":"60"},"required":true,"description":"Whole seconds until the caller may retry"},"X-RateLimit-Limit":{"schema":{"type":"string","description":"Request quota for the current window. Sent only by limiters that publish one."},"required":false,"description":"Request quota for the current window. Sent only by limiters that publish one."},"X-RateLimit-Remaining":{"schema":{"type":"string","description":"Requests remaining in the current window. Sent only by limiters that publish one."},"required":false,"description":"Requests remaining in the current window. Sent only by limiters that publish one."},"X-RateLimit-Reset":{"schema":{"type":"string","description":"UNIX timestamp in seconds when the window resets. Sent only by limiters that publish one."},"required":false,"description":"UNIX timestamp in seconds when the window resets. Sent only by limiters that publish one."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RateLimitError"}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InternalError"}}}}},"x-cli-group":"invoices","x-cli-name":"get-pdf","x-codeSamples":[{"lang":"curl","label":"cURL","source":"curl -X GET \"https://api.revkeen.com/v2/invoices/11111111-1111-4111-8111-111111111111/pdf\" \\\n  -H \"x-api-key: $REVKEEN_API_KEY\""},{"lang":"typescript","label":"TypeScript (HTTP)","source":"const credential = process.env.REVKEEN_API_KEY;\nif (!credential) throw new Error(\"Set REVKEEN_API_KEY\");\nconst response = await fetch(\"https://api.revkeen.com/v2/invoices/11111111-1111-4111-8111-111111111111/pdf\", {\n  method: \"GET\",\n  headers: {\n    \"x-api-key\": credential,\n  },\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"go","label":"Go (HTTP)","source":"package main\nimport (\"fmt\"; \"io\"; \"net/http\"; \"os\"; \"strings\"; \"time\")\nfunc main() {\n  req, err := http.NewRequest(\"GET\", \"https://api.revkeen.com/v2/invoices/11111111-1111-4111-8111-111111111111/pdf\", strings.NewReader(\"\"))\n  if err != nil { panic(err) }\n  req.Header.Set(\"x-api-key\", os.Getenv(\"REVKEEN_API_KEY\"))\n  req.Header.Set(\"Content-Type\", \"application/json\")\n  client := &http.Client{Timeout: 10 * time.Second}\n  response, err := client.Do(req)\n  if err != nil { panic(err) }\n  defer response.Body.Close()\n  if response.StatusCode >= 400 { panic(fmt.Sprintf(\"RevKeen request failed (%d)\", response.StatusCode)) }\n  data, err := io.ReadAll(response.Body)\n  if err != nil { panic(err) }\n  fmt.Println(string(data))\n}"},{"lang":"php","label":"PHP (HTTP)","source":"<?php\n$request = curl_init(\"https://api.revkeen.com/v2/invoices/11111111-1111-4111-8111-111111111111/pdf\");\ncurl_setopt_array($request, [\n  CURLOPT_CUSTOMREQUEST => \"GET\",\n  CURLOPT_RETURNTRANSFER => true,\n  CURLOPT_TIMEOUT => 10,\n  CURLOPT_HTTPHEADER => [\"x-api-key: \" . getenv(\"REVKEEN_API_KEY\"), \"Content-Type: application/json\"],\n]);\n$response = curl_exec($request);\nif ($response === false) throw new RuntimeException(curl_error($request));\n$status = curl_getinfo($request, CURLINFO_HTTP_CODE);\ncurl_close($request);\nif ($status >= 400) throw new RuntimeException(\"RevKeen request failed ($status)\");\necho $response;"},{"lang":"python","label":"Python","source":"import os\n\nimport requests\n\nresponse = requests.get(\n    \"https://api.revkeen.com/v2/invoices/11111111-1111-4111-8111-111111111111/pdf\",\n    headers={\"x-api-key\": os.environ[\"REVKEEN_API_KEY\"]},\n)\n\nresponse.raise_for_status()\nprint(response.json())"},{"lang":"javascript","label":"JavaScript","source":"const credential = process.env.REVKEEN_API_KEY;\nif (!credential) throw new Error(\"Set REVKEEN_API_KEY\");\nconst response = await fetch(\"https://api.revkeen.com/v2/invoices/11111111-1111-4111-8111-111111111111/pdf\", {\n  method: \"GET\",\n  headers: {\n    \"x-api-key\": credential,\n  },\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"java","label":"Java","source":"import java.net.URI;\nimport java.net.http.HttpClient;\nimport java.net.http.HttpRequest;\nimport java.net.http.HttpResponse;\n\nHttpClient client = HttpClient.newHttpClient();\n\nHttpRequest request = HttpRequest.newBuilder()\n    .uri(URI.create(\"https://api.revkeen.com/v2/invoices/11111111-1111-4111-8111-111111111111/pdf\"))\n    .header(\"x-api-key\", System.getenv(\"REVKEEN_API_KEY\"))\n    .GET()\n    .build();\n\nHttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());\nSystem.out.println(response.body());"},{"lang":"ruby","label":"Ruby","source":"require \"net/http\"\nrequire \"json\"\nrequire \"uri\"\n\nuri = URI(\"https://api.revkeen.com/v2/invoices/11111111-1111-4111-8111-111111111111/pdf\")\nhttp = Net::HTTP.new(uri.host, uri.port)\nhttp.use_ssl = true\n\nrequest = Net::HTTP::Get.new(uri)\nrequest[\"x-api-key\"] = ENV[\"REVKEEN_API_KEY\"]\n\nresponse = http.request(request)\nputs response.body"}],"security":[{"apiKey":["invoices:read"]},{"oauth2":["invoices:read"]}]}},"/invoices/{id}/finalize":{"post":{"operationId":"invoices_finalize","tags":["Invoices"],"x-fern-sdk-group-name":["invoices"],"summary":"Finalize an invoice","description":"Finalizes a draft invoice: its status becomes `finalized`, it gets a public token, and its financial fields become immutable. A draft with no line items can be finalized when its `total_minor` is above zero; a zero total is refused.\n\n---\n\n**Related endpoints**\n\n- `GET /invoices/{id}/usage-explain` — Explain invoice usage charges\n- `GET /invoices` — List invoices\n- `POST /invoices` — Create invoice\n- `GET /invoices/{id}` — Get invoice\n- `PATCH /invoices/{id}` — Update invoice\n- `GET /invoices/{id}/pdf` — Get invoice PDF link\n- `POST /invoices/{id}/void` — Void an invoice\n- `POST /invoices/{id}/send` — Send an invoice\n\n**Common errors**\n\n- `400 invalid_request` — malformed payload or failed validation.\n- `404 resource_missing` — the referenced resource does not exist or is not visible to your key.\n- `409 conflict` — Idempotency-Key collision with a different body, or a concurrent state-transition conflict.\n\n**Idempotency**\n\nPass an `Idempotency-Key` header (UUID v4 recommended) to make retries safe. Keys are valid for 24 hours; see [the idempotency guide](/docs/fundamentals/idempotency).","parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"}],"requestBody":{"description":"Finalization options","required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"auto_advance":{"type":"boolean","description":"Accepted for compatibility and has no effect: a finalized invoice can already be sent and paid.","example":false}}}}}},"responses":{"200":{"description":"Invoice finalized successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InvoiceResponse"}}}},"400":{"description":"Invoice cannot be finalized: its total is zero, or it has no invoice number","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationError"}}}},"401":{"description":"Unauthorized - Invalid or missing API key","headers":{"WWW-Authenticate":{"description":"RFC 6750 Bearer authentication challenge","schema":{"type":"string"},"example":"Bearer realm=\"revkeen-api\""}}},"404":{"description":"Invoice not found (resource_not_found)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotFoundError"}}}},"409":{"description":"Invoice is not a draft (invoice_already_finalized, invoice_already_voided or invoice_already_paid)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConflictError"}}}},"429":{"description":"Too Many Requests - Rate limit exceeded","headers":{"Retry-After":{"description":"Whole seconds until the caller may retry","schema":{"type":"string"},"example":"60"},"X-RateLimit-Limit":{"description":"Request quota for the current window","schema":{"type":"string"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window","schema":{"type":"string"}},"X-RateLimit-Reset":{"description":"UNIX timestamp in seconds when the window resets","schema":{"type":"string"}}}}},"x-cli-group":"invoices","x-cli-name":"finalize","x-codeSamples":[{"lang":"curl","label":"cURL","source":"curl -X POST \"https://api.revkeen.com/v2/invoices/11111111-1111-4111-8111-111111111111/finalize\" \\\n  -H \"x-api-key: $REVKEEN_API_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"auto_advance\": false\n  }'"},{"lang":"typescript","label":"TypeScript (SDK)","source":"// npm install @revkeen/sdk@1.20260822.1541\nimport { RevKeenClient } from \"@revkeen/sdk\";\nconst apiKey = process.env.REVKEEN_API_KEY;\nif (!apiKey) throw new Error(\"Set REVKEEN_API_KEY\");\nconst client = new RevKeenClient({ apiKey, baseUrl: \"https://api.revkeen.com/v2\" });\nconst result = await client.invoices.finalize(\"11111111-1111-4111-8111-111111111111\", {\n  \"auto_advance\": false\n});\nconsole.log(result);"},{"lang":"go","label":"Go (HTTP)","source":"package main\nimport (\"fmt\"; \"io\"; \"net/http\"; \"os\"; \"strings\"; \"time\")\nfunc main() {\n  req, err := http.NewRequest(\"POST\", \"https://api.revkeen.com/v2/invoices/11111111-1111-4111-8111-111111111111/finalize\", strings.NewReader(\"{\\\"auto_advance\\\":false}\"))\n  if err != nil { panic(err) }\n  req.Header.Set(\"x-api-key\", os.Getenv(\"REVKEEN_API_KEY\"))\n  req.Header.Set(\"Content-Type\", \"application/json\")\n  client := &http.Client{Timeout: 10 * time.Second}\n  response, err := client.Do(req)\n  if err != nil { panic(err) }\n  defer response.Body.Close()\n  if response.StatusCode >= 400 { panic(fmt.Sprintf(\"RevKeen request failed (%d)\", response.StatusCode)) }\n  data, err := io.ReadAll(response.Body)\n  if err != nil { panic(err) }\n  fmt.Println(string(data))\n}"},{"lang":"php","label":"PHP (HTTP)","source":"<?php\n$request = curl_init(\"https://api.revkeen.com/v2/invoices/11111111-1111-4111-8111-111111111111/finalize\");\ncurl_setopt_array($request, [\n  CURLOPT_CUSTOMREQUEST => \"POST\",\n  CURLOPT_RETURNTRANSFER => true,\n  CURLOPT_TIMEOUT => 10,\n  CURLOPT_HTTPHEADER => [\"x-api-key: \" . getenv(\"REVKEEN_API_KEY\"), \"Content-Type: application/json\"],\n  CURLOPT_POSTFIELDS => '{\\\"auto_advance\\\":false}',\n]);\n$response = curl_exec($request);\nif ($response === false) throw new RuntimeException(curl_error($request));\n$status = curl_getinfo($request, CURLINFO_HTTP_CODE);\ncurl_close($request);\nif ($status >= 400) throw new RuntimeException(\"RevKeen request failed ($status)\");\necho $response;"},{"lang":"python","label":"Python","source":"import os\n\nimport requests\n\nresponse = requests.post(\n    \"https://api.revkeen.com/v2/invoices/11111111-1111-4111-8111-111111111111/finalize\",\n    headers={\"x-api-key\": os.environ[\"REVKEEN_API_KEY\"]},\n    json={\n        \"auto_advance\": False,\n    },\n)\n\nresponse.raise_for_status()\nprint(response.json())"},{"lang":"javascript","label":"JavaScript","source":"const credential = process.env.REVKEEN_API_KEY;\nif (!credential) throw new Error(\"Set REVKEEN_API_KEY\");\nconst response = await fetch(\"https://api.revkeen.com/v2/invoices/11111111-1111-4111-8111-111111111111/finalize\", {\n  method: \"POST\",\n  headers: {\n    \"x-api-key\": credential,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n    \"auto_advance\": false\n  }),\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"java","label":"Java","source":"import java.net.URI;\nimport java.net.http.HttpClient;\nimport java.net.http.HttpRequest;\nimport java.net.http.HttpResponse;\n\nHttpClient client = HttpClient.newHttpClient();\n\nHttpRequest request = HttpRequest.newBuilder()\n    .uri(URI.create(\"https://api.revkeen.com/v2/invoices/11111111-1111-4111-8111-111111111111/finalize\"))\n    .header(\"x-api-key\", System.getenv(\"REVKEEN_API_KEY\"))\n    .header(\"Content-Type\", \"application/json\")\n    .method(\"POST\", HttpRequest.BodyPublishers.ofString(\"\"\"\n        {\n          \"auto_advance\": false\n        }\n        \"\"\"))\n    .build();\n\nHttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());\nSystem.out.println(response.body());"},{"lang":"ruby","label":"Ruby","source":"require \"net/http\"\nrequire \"json\"\nrequire \"uri\"\n\nuri = URI(\"https://api.revkeen.com/v2/invoices/11111111-1111-4111-8111-111111111111/finalize\")\nhttp = Net::HTTP.new(uri.host, uri.port)\nhttp.use_ssl = true\n\nrequest = Net::HTTP::Post.new(uri)\nrequest[\"x-api-key\"] = ENV[\"REVKEEN_API_KEY\"]\nrequest[\"Content-Type\"] = \"application/json\"\nrequest.body = {\n  \"auto_advance\" => false,\n}.to_json\n\nresponse = http.request(request)\nputs response.body"}],"security":[{"apiKey":["invoices:write"]},{"oauth2":["invoices:write"]}]}},"/invoices/{id}/void":{"post":{"operationId":"invoices_void","tags":["Invoices"],"x-fern-sdk-group-name":["invoices"],"summary":"Void an invoice","description":"Voids an invoice. Only invoices without recorded payments can be voided. Use refund instead for paid invoices.\n\n---\n\n**Related endpoints**\n\n- `GET /invoices/{id}/usage-explain` — Explain invoice usage charges\n- `GET /invoices` — List invoices\n- `POST /invoices` — Create invoice\n- `GET /invoices/{id}` — Get invoice\n- `PATCH /invoices/{id}` — Update invoice\n- `GET /invoices/{id}/pdf` — Get invoice PDF link\n- `POST /invoices/{id}/finalize` — Finalize an invoice\n- `POST /invoices/{id}/send` — Send an invoice\n\n**Common errors**\n\n- `400 invalid_request` — malformed payload or failed validation.\n- `404 resource_missing` — the referenced resource does not exist or is not visible to your key.\n- `409 conflict` — Idempotency-Key collision with a different body, or a concurrent state-transition conflict.\n\n**Idempotency**\n\nPass an `Idempotency-Key` header (UUID v4 recommended) to make retries safe. Keys are valid for 24 hours; see [the idempotency guide](/docs/fundamentals/idempotency).","parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"}],"requestBody":{"description":"Void details","required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"reason":{"type":"string","maxLength":500,"description":"Reason for voiding the invoice","example":"Customer requested cancellation"}}}}}},"responses":{"200":{"description":"Invoice voided successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InvoiceResponse"}}}},"400":{"description":"Invoice cannot be voided (it has payments, or its status cannot be voided)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationError"}}}},"401":{"description":"Unauthorized - Invalid or missing API key","headers":{"WWW-Authenticate":{"description":"RFC 6750 Bearer authentication challenge","schema":{"type":"string"},"example":"Bearer realm=\"revkeen-api\""}}},"404":{"description":"Invoice not found (resource_not_found)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotFoundError"}}}},"409":{"description":"The invoice's status conflicts with this action (invoice_already_finalized, invoice_already_voided or invoice_already_paid)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConflictError"}}}},"429":{"description":"Too Many Requests - Rate limit exceeded","headers":{"Retry-After":{"description":"Whole seconds until the caller may retry","schema":{"type":"string"},"example":"60"},"X-RateLimit-Limit":{"description":"Request quota for the current window","schema":{"type":"string"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window","schema":{"type":"string"}},"X-RateLimit-Reset":{"description":"UNIX timestamp in seconds when the window resets","schema":{"type":"string"}}}}},"x-cli-group":"invoices","x-cli-name":"void","x-codeSamples":[{"lang":"curl","label":"cURL","source":"curl -X POST \"https://api.revkeen.com/v2/invoices/11111111-1111-4111-8111-111111111111/void\" \\\n  -H \"x-api-key: $REVKEEN_API_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"reason\": \"Customer requested cancellation\"\n  }'"},{"lang":"typescript","label":"TypeScript (SDK)","source":"// npm install @revkeen/sdk@1.20260822.1541\nimport { RevKeenClient } from \"@revkeen/sdk\";\nconst apiKey = process.env.REVKEEN_API_KEY;\nif (!apiKey) throw new Error(\"Set REVKEEN_API_KEY\");\nconst client = new RevKeenClient({ apiKey, baseUrl: \"https://api.revkeen.com/v2\" });\nconst result = await client.invoices.void(\"11111111-1111-4111-8111-111111111111\", {\n  \"reason\": \"Customer requested cancellation\"\n});\nconsole.log(result);"},{"lang":"go","label":"Go (HTTP)","source":"package main\nimport (\"fmt\"; \"io\"; \"net/http\"; \"os\"; \"strings\"; \"time\")\nfunc main() {\n  req, err := http.NewRequest(\"POST\", \"https://api.revkeen.com/v2/invoices/11111111-1111-4111-8111-111111111111/void\", strings.NewReader(\"{\\\"reason\\\":\\\"Customer requested cancellation\\\"}\"))\n  if err != nil { panic(err) }\n  req.Header.Set(\"x-api-key\", os.Getenv(\"REVKEEN_API_KEY\"))\n  req.Header.Set(\"Content-Type\", \"application/json\")\n  client := &http.Client{Timeout: 10 * time.Second}\n  response, err := client.Do(req)\n  if err != nil { panic(err) }\n  defer response.Body.Close()\n  if response.StatusCode >= 400 { panic(fmt.Sprintf(\"RevKeen request failed (%d)\", response.StatusCode)) }\n  data, err := io.ReadAll(response.Body)\n  if err != nil { panic(err) }\n  fmt.Println(string(data))\n}"},{"lang":"php","label":"PHP (HTTP)","source":"<?php\n$request = curl_init(\"https://api.revkeen.com/v2/invoices/11111111-1111-4111-8111-111111111111/void\");\ncurl_setopt_array($request, [\n  CURLOPT_CUSTOMREQUEST => \"POST\",\n  CURLOPT_RETURNTRANSFER => true,\n  CURLOPT_TIMEOUT => 10,\n  CURLOPT_HTTPHEADER => [\"x-api-key: \" . getenv(\"REVKEEN_API_KEY\"), \"Content-Type: application/json\"],\n  CURLOPT_POSTFIELDS => '{\\\"reason\\\":\\\"Customer requested cancellation\\\"}',\n]);\n$response = curl_exec($request);\nif ($response === false) throw new RuntimeException(curl_error($request));\n$status = curl_getinfo($request, CURLINFO_HTTP_CODE);\ncurl_close($request);\nif ($status >= 400) throw new RuntimeException(\"RevKeen request failed ($status)\");\necho $response;"},{"lang":"python","label":"Python","source":"import os\n\nimport requests\n\nresponse = requests.post(\n    \"https://api.revkeen.com/v2/invoices/11111111-1111-4111-8111-111111111111/void\",\n    headers={\"x-api-key\": os.environ[\"REVKEEN_API_KEY\"]},\n    json={\n        \"reason\": \"Customer requested cancellation\",\n    },\n)\n\nresponse.raise_for_status()\nprint(response.json())"},{"lang":"javascript","label":"JavaScript","source":"const credential = process.env.REVKEEN_API_KEY;\nif (!credential) throw new Error(\"Set REVKEEN_API_KEY\");\nconst response = await fetch(\"https://api.revkeen.com/v2/invoices/11111111-1111-4111-8111-111111111111/void\", {\n  method: \"POST\",\n  headers: {\n    \"x-api-key\": credential,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n    \"reason\": \"Customer requested cancellation\"\n  }),\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"java","label":"Java","source":"import java.net.URI;\nimport java.net.http.HttpClient;\nimport java.net.http.HttpRequest;\nimport java.net.http.HttpResponse;\n\nHttpClient client = HttpClient.newHttpClient();\n\nHttpRequest request = HttpRequest.newBuilder()\n    .uri(URI.create(\"https://api.revkeen.com/v2/invoices/11111111-1111-4111-8111-111111111111/void\"))\n    .header(\"x-api-key\", System.getenv(\"REVKEEN_API_KEY\"))\n    .header(\"Content-Type\", \"application/json\")\n    .method(\"POST\", HttpRequest.BodyPublishers.ofString(\"\"\"\n        {\n          \"reason\": \"Customer requested cancellation\"\n        }\n        \"\"\"))\n    .build();\n\nHttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());\nSystem.out.println(response.body());"},{"lang":"ruby","label":"Ruby","source":"require \"net/http\"\nrequire \"json\"\nrequire \"uri\"\n\nuri = URI(\"https://api.revkeen.com/v2/invoices/11111111-1111-4111-8111-111111111111/void\")\nhttp = Net::HTTP.new(uri.host, uri.port)\nhttp.use_ssl = true\n\nrequest = Net::HTTP::Post.new(uri)\nrequest[\"x-api-key\"] = ENV[\"REVKEEN_API_KEY\"]\nrequest[\"Content-Type\"] = \"application/json\"\nrequest.body = {\n  \"reason\" => \"Customer requested cancellation\",\n}.to_json\n\nresponse = http.request(request)\nputs response.body"}],"security":[{"apiKey":["invoices:write"]},{"oauth2":["invoices:write"]}]}},"/invoices/{id}/send":{"post":{"operationId":"invoices_send","tags":["Invoices"],"x-fern-sdk-group-name":["invoices"],"summary":"Send an invoice","description":"Sends an invoice to the customer via the specified channel (email, SMS, or WhatsApp). A draft is approved first; a finalized invoice becomes `sent`. The send is refused, and nothing changes, when the invoice has no customer, the customer has no address for the channel, or nothing is due.\n\n---\n\n**Related endpoints**\n\n- `GET /invoices/{id}/usage-explain` — Explain invoice usage charges\n- `GET /invoices` — List invoices\n- `POST /invoices` — Create invoice\n- `GET /invoices/{id}` — Get invoice\n- `PATCH /invoices/{id}` — Update invoice\n- `GET /invoices/{id}/pdf` — Get invoice PDF link\n- `POST /invoices/{id}/finalize` — Finalize an invoice\n- `POST /invoices/{id}/void` — Void an invoice\n\n**Common errors**\n\n- `400 invalid_request` — malformed payload or failed validation.\n- `404 resource_missing` — the referenced resource does not exist or is not visible to your key.\n- `409 conflict` — Idempotency-Key collision with a different body, or a concurrent state-transition conflict.\n\n**Idempotency**\n\nPass an `Idempotency-Key` header (UUID v4 recommended) to make retries safe. Keys are valid for 24 hours; see [the idempotency guide](/docs/fundamentals/idempotency).","parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"}],"requestBody":{"description":"Send options","required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"channel":{"type":"string","enum":["email","sms","whatsapp"],"default":"email","description":"Communication channel to send the invoice through","example":"email"},"template_id":{"type":"string","format":"uuid","description":"Optional custom template ID to use for the notification"}}}}}},"responses":{"200":{"description":"Invoice sent successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InvoiceResponse"}}}},"400":{"description":"Invoice cannot be sent: its status cannot be sent, it has no customer, the customer has no address for the channel, or nothing is due","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationError"}}}},"401":{"description":"Unauthorized - Invalid or missing API key","headers":{"WWW-Authenticate":{"description":"RFC 6750 Bearer authentication challenge","schema":{"type":"string"},"example":"Bearer realm=\"revkeen-api\""}}},"404":{"description":"Invoice not found (resource_not_found)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotFoundError"}}}},"409":{"description":"The invoice's status conflicts with this action (invoice_already_finalized, invoice_already_voided or invoice_already_paid)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConflictError"}}}},"429":{"description":"Too Many Requests - Rate limit exceeded","headers":{"Retry-After":{"description":"Whole seconds until the caller may retry","schema":{"type":"string"},"example":"60"},"X-RateLimit-Limit":{"description":"Request quota for the current window","schema":{"type":"string"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window","schema":{"type":"string"}},"X-RateLimit-Reset":{"description":"UNIX timestamp in seconds when the window resets","schema":{"type":"string"}}}},"503":{"description":"The invoice's status was updated but its delivery could not be queued; retry the send"}},"x-cli-group":"invoices","x-cli-name":"send","x-codeSamples":[{"lang":"curl","label":"cURL","source":"curl -X POST \"https://api.revkeen.com/v2/invoices/11111111-1111-4111-8111-111111111111/send\" \\\n  -H \"x-api-key: $REVKEEN_API_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"channel\": \"email\",\n    \"template_id\": \"11111111-1111-4111-8111-111111111111\"\n  }'"},{"lang":"typescript","label":"TypeScript (SDK)","source":"// npm install @revkeen/sdk@1.20260822.1541\nimport { RevKeenClient } from \"@revkeen/sdk\";\nconst apiKey = process.env.REVKEEN_API_KEY;\nif (!apiKey) throw new Error(\"Set REVKEEN_API_KEY\");\nconst client = new RevKeenClient({ apiKey, baseUrl: \"https://api.revkeen.com/v2\" });\nconst result = await client.invoices.send(\"11111111-1111-4111-8111-111111111111\", {\n  \"channel\": \"email\",\n  \"template_id\": \"11111111-1111-4111-8111-111111111111\"\n});\nconsole.log(result);"},{"lang":"go","label":"Go (HTTP)","source":"package main\nimport (\"fmt\"; \"io\"; \"net/http\"; \"os\"; \"strings\"; \"time\")\nfunc main() {\n  req, err := http.NewRequest(\"POST\", \"https://api.revkeen.com/v2/invoices/11111111-1111-4111-8111-111111111111/send\", strings.NewReader(\"{\\\"channel\\\":\\\"email\\\",\\\"template_id\\\":\\\"11111111-1111-4111-8111-111111111111\\\"}\"))\n  if err != nil { panic(err) }\n  req.Header.Set(\"x-api-key\", os.Getenv(\"REVKEEN_API_KEY\"))\n  req.Header.Set(\"Content-Type\", \"application/json\")\n  client := &http.Client{Timeout: 10 * time.Second}\n  response, err := client.Do(req)\n  if err != nil { panic(err) }\n  defer response.Body.Close()\n  if response.StatusCode >= 400 { panic(fmt.Sprintf(\"RevKeen request failed (%d)\", response.StatusCode)) }\n  data, err := io.ReadAll(response.Body)\n  if err != nil { panic(err) }\n  fmt.Println(string(data))\n}"},{"lang":"php","label":"PHP (HTTP)","source":"<?php\n$request = curl_init(\"https://api.revkeen.com/v2/invoices/11111111-1111-4111-8111-111111111111/send\");\ncurl_setopt_array($request, [\n  CURLOPT_CUSTOMREQUEST => \"POST\",\n  CURLOPT_RETURNTRANSFER => true,\n  CURLOPT_TIMEOUT => 10,\n  CURLOPT_HTTPHEADER => [\"x-api-key: \" . getenv(\"REVKEEN_API_KEY\"), \"Content-Type: application/json\"],\n  CURLOPT_POSTFIELDS => '{\\\"channel\\\":\\\"email\\\",\\\"template_id\\\":\\\"11111111-1111-4111-8111-111111111111\\\"}',\n]);\n$response = curl_exec($request);\nif ($response === false) throw new RuntimeException(curl_error($request));\n$status = curl_getinfo($request, CURLINFO_HTTP_CODE);\ncurl_close($request);\nif ($status >= 400) throw new RuntimeException(\"RevKeen request failed ($status)\");\necho $response;"},{"lang":"python","label":"Python","source":"import os\n\nimport requests\n\nresponse = requests.post(\n    \"https://api.revkeen.com/v2/invoices/11111111-1111-4111-8111-111111111111/send\",\n    headers={\"x-api-key\": os.environ[\"REVKEEN_API_KEY\"]},\n    json={\n        \"channel\": \"email\",\n        \"template_id\": \"11111111-1111-4111-8111-111111111111\",\n    },\n)\n\nresponse.raise_for_status()\nprint(response.json())"},{"lang":"javascript","label":"JavaScript","source":"const credential = process.env.REVKEEN_API_KEY;\nif (!credential) throw new Error(\"Set REVKEEN_API_KEY\");\nconst response = await fetch(\"https://api.revkeen.com/v2/invoices/11111111-1111-4111-8111-111111111111/send\", {\n  method: \"POST\",\n  headers: {\n    \"x-api-key\": credential,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n    \"channel\": \"email\",\n    \"template_id\": \"11111111-1111-4111-8111-111111111111\"\n  }),\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"java","label":"Java","source":"import java.net.URI;\nimport java.net.http.HttpClient;\nimport java.net.http.HttpRequest;\nimport java.net.http.HttpResponse;\n\nHttpClient client = HttpClient.newHttpClient();\n\nHttpRequest request = HttpRequest.newBuilder()\n    .uri(URI.create(\"https://api.revkeen.com/v2/invoices/11111111-1111-4111-8111-111111111111/send\"))\n    .header(\"x-api-key\", System.getenv(\"REVKEEN_API_KEY\"))\n    .header(\"Content-Type\", \"application/json\")\n    .method(\"POST\", HttpRequest.BodyPublishers.ofString(\"\"\"\n        {\n          \"channel\": \"email\",\n          \"template_id\": \"11111111-1111-4111-8111-111111111111\"\n        }\n        \"\"\"))\n    .build();\n\nHttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());\nSystem.out.println(response.body());"},{"lang":"ruby","label":"Ruby","source":"require \"net/http\"\nrequire \"json\"\nrequire \"uri\"\n\nuri = URI(\"https://api.revkeen.com/v2/invoices/11111111-1111-4111-8111-111111111111/send\")\nhttp = Net::HTTP.new(uri.host, uri.port)\nhttp.use_ssl = true\n\nrequest = Net::HTTP::Post.new(uri)\nrequest[\"x-api-key\"] = ENV[\"REVKEEN_API_KEY\"]\nrequest[\"Content-Type\"] = \"application/json\"\nrequest.body = {\n  \"channel\" => \"email\",\n  \"template_id\" => \"11111111-1111-4111-8111-111111111111\",\n}.to_json\n\nresponse = http.request(request)\nputs response.body"}],"security":[{"apiKey":["invoices:write"]},{"oauth2":["invoices:write"]}]}},"/invoices/{id}/pay":{"post":{"operationId":"invoices_pay","tags":["Invoices"],"security":[{"apiKey":["invoices:write"]}],"x-fern-sdk-group-name":["invoices"],"summary":"Record or collect a payment","description":"Records a payment the customer made outside RevKeen (`mode: \"record\"`), or asks RevKeen to collect the invoice by Direct Debit (`mode: \"collect\"`).\n\n### Record\n\nRecords a payment the customer made outside RevKeen, such as a bank transfer, cash or a cheque, and applies it to the invoice. RevKeen collects no money in this mode.\n\n`mode`, `payment_method`, `reference` and `amount_minor` are all required and nothing is defaulted. A payment that covers the whole outstanding amount moves the invoice to `paid`; a smaller one moves it to `partially_paid`. An amount larger than the invoice total less what is already paid is refused.\n\nA payment record is written for every call, and an invoice is never marked paid without one. To mark an invoice paid, use this operation: setting `status` to `paid` or `partially_paid` through `PATCH /v2/invoices/{id}` is refused.\n\n**Refused with 409, and nothing is changed**, when the invoice is already paid, void, a draft, or not recordable here, or when a Direct Debit collection for it is still pending (cancel the collection first; recording a payment would let the customer be charged twice).\n\nThe recorded payment fires the same events as a payment recorded in the dashboard, including `invoice.paid` and `payment.succeeded`. Recording requires the `invoice:mark_paid` permission.\n\n### Collect by Direct Debit\n\nSchedules a Direct Debit collection of the invoice's **outstanding amount** (the amount due) against the customer's active mandate, and returns `202`. Only `payment_method: \"direct_debit\"` is accepted; card collection is not available through this operation. The amount is never taken from the request: a body with any other field, such as `amount_minor`, is refused with `400`.\n\nThe collection is scheduled, not completed. **The invoice stays unpaid** and becomes paid only when the collection settles. The customer is sent the Direct Debit advance notice first; when that notice is still being delivered, `collection.status` is `awaiting_advance_notice` and the collection is submitted once delivery is confirmed.\n\n**Refused with 409, and nothing is scheduled**, when the invoice is already paid, void, a draft or not collectable, when a Direct Debit collection for it is already pending (`invoice_has_pending_dd_collection`), when the customer has no usable Direct Debit mandate (`mandate_not_usable`, including more than one active mandate), or when Direct Debit is not enabled for your account (`direct_debit_not_configured`).\n\nCollecting requires the `direct_debit:submit_collection` permission and, for an API key, the `mandates:write` scope as well as `invoices:write`. An API key is decided as the team member who issued it. Your account owners and admins are emailed whenever an API key starts a collection.\n\n### Both modes\n\nRequires the `invoices:write` scope. Send an `Idempotency-Key` header: a replay with the same key records or schedules nothing a second time and returns the stored response. A response, including an error, is stored for the key, so use a new key to retry after a failure.\n\n---\n\n**Related endpoints**\n\n- `GET /invoices/{id}/usage-explain` — Explain invoice usage charges\n- `GET /invoices` — List invoices\n- `POST /invoices` — Create invoice\n- `GET /invoices/{id}` — Get invoice\n- `PATCH /invoices/{id}` — Update invoice\n- `GET /invoices/{id}/pdf` — Get invoice PDF link\n- `POST /invoices/{id}/finalize` — Finalize an invoice\n- `POST /invoices/{id}/void` — Void an invoice\n\n**Common errors**\n\n- `400 invalid_request` — malformed payload or failed validation.\n- `401 authentication_error` — missing, invalid, expired, or revoked credential. Codes: `authentication_failed`, `invalid_api_key`, `expired_api_key`, `api_key_revoked`, `session_invalid`, `merchant_required`. Carries a `WWW-Authenticate: Bearer` challenge.\n- `403 authorization_error` — the caller is identified but the action is denied. Codes include `insufficient_permissions`, `ip_not_allowed`, `merchant_mismatch`, and `origin_not_allowed`.\n- `404 resource_missing` — the referenced resource does not exist or is not visible to your key.\n- `409 conflict` — Idempotency-Key collision with a different body, or a concurrent state-transition conflict.\n- `429 rate_limit_error` — code `rate_limit_exceeded`. Back off using the `Retry-After` header (whole seconds); `X-RateLimit-Reset` is a UNIX timestamp in seconds.\n\n**Idempotency**\n\nPass an `Idempotency-Key` header (UUID v4 recommended) to make retries safe. Keys are valid for 24 hours; see [the idempotency guide](/docs/fundamentals/idempotency).","parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"}],"requestBody":{"description":"Payment details","required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PayInvoiceRequest"}}}},"responses":{"200":{"description":"Payment recorded (mode `record`); the invoice as it now stands","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InvoiceResponse"}}}},"202":{"description":"Direct Debit collection scheduled (mode `collect`). The invoice is not paid until the collection settles.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InvoiceCollectionAcceptedResponse"}}}},"400":{"description":"Bad Request - Validation error or invalid parameters","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationError"}}}},"401":{"description":"Unauthorized - Invalid or missing API key","headers":{"WWW-Authenticate":{"schema":{"type":"string","description":"RFC 6750 Bearer authentication challenge","example":"Bearer realm=\"revkeen-api\""},"required":true,"description":"RFC 6750 Bearer authentication challenge"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthenticationError"}}}},"403":{"description":"Insufficient scope (`invoices:write`; for collect, an API key also needs `mandates:write`), or the authorization policy denied `invoice:mark_paid` (record) or `direct_debit:submit_collection` (collect) for the caller","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MerchantActionForbiddenError"}}}},"404":{"description":"Not Found - Resource does not exist","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotFoundError"}}}},"409":{"description":"The invoice cannot take this payment or collection (already paid, void, draft, not recordable or collectable here, a Direct Debit collection is pending, no usable mandate, or Direct Debit not enabled). Nothing was changed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InvoicePayConflictError"}}}},"429":{"description":"Too Many Requests - Rate limit exceeded","headers":{"Retry-After":{"schema":{"type":"string","description":"Whole seconds until the caller may retry","example":"60"},"required":true,"description":"Whole seconds until the caller may retry"},"X-RateLimit-Limit":{"schema":{"type":"string","description":"Request quota for the current window. Sent only by limiters that publish one."},"required":false,"description":"Request quota for the current window. Sent only by limiters that publish one."},"X-RateLimit-Remaining":{"schema":{"type":"string","description":"Requests remaining in the current window. Sent only by limiters that publish one."},"required":false,"description":"Requests remaining in the current window. Sent only by limiters that publish one."},"X-RateLimit-Reset":{"schema":{"type":"string","description":"UNIX timestamp in seconds when the window resets. Sent only by limiters that publish one."},"required":false,"description":"UNIX timestamp in seconds when the window resets. Sent only by limiters that publish one."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RateLimitError"}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InternalError"}}}},"502":{"description":"Collect only: the Direct Debit bureau or the advance-notice step refused or failed, so no collection was scheduled (`gateway_error`). Retry later with a new Idempotency-Key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiError"}}}},"503":{"description":"A required audit record could not be written; the payment was not recorded and no collection was scheduled. Retry later with a new Idempotency-Key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthorizationAuditUnavailableError"}}}}},"x-cli-group":"invoices","x-cli-name":"pay","x-codeSamples":[{"lang":"curl","label":"cURL","source":"curl -X POST \"https://api.revkeen.com/v2/invoices/11111111-1111-4111-8111-111111111111/pay\" \\\n  -H \"x-api-key: $REVKEEN_API_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"mode\": \"record\",\n    \"payment_method\": \"bank_transfer\",\n    \"reference\": \"CHK-12345\",\n    \"amount_minor\": 10000\n  }'"},{"lang":"typescript","label":"TypeScript (HTTP)","source":"const credential = process.env.REVKEEN_API_KEY;\nif (!credential) throw new Error(\"Set REVKEEN_API_KEY\");\nconst response = await fetch(\"https://api.revkeen.com/v2/invoices/11111111-1111-4111-8111-111111111111/pay\", {\n  method: \"POST\",\n  headers: {\n    \"x-api-key\": credential,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n    \"mode\": \"record\",\n    \"payment_method\": \"bank_transfer\",\n    \"reference\": \"CHK-12345\",\n    \"amount_minor\": 10000\n  }),\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"go","label":"Go (HTTP)","source":"package main\nimport (\"fmt\"; \"io\"; \"net/http\"; \"os\"; \"strings\"; \"time\")\nfunc main() {\n  req, err := http.NewRequest(\"POST\", \"https://api.revkeen.com/v2/invoices/11111111-1111-4111-8111-111111111111/pay\", strings.NewReader(\"{\\\"mode\\\":\\\"record\\\",\\\"payment_method\\\":\\\"bank_transfer\\\",\\\"reference\\\":\\\"CHK-12345\\\",\\\"amount_minor\\\":10000}\"))\n  if err != nil { panic(err) }\n  req.Header.Set(\"x-api-key\", os.Getenv(\"REVKEEN_API_KEY\"))\n  req.Header.Set(\"Content-Type\", \"application/json\")\n  client := &http.Client{Timeout: 10 * time.Second}\n  response, err := client.Do(req)\n  if err != nil { panic(err) }\n  defer response.Body.Close()\n  if response.StatusCode >= 400 { panic(fmt.Sprintf(\"RevKeen request failed (%d)\", response.StatusCode)) }\n  data, err := io.ReadAll(response.Body)\n  if err != nil { panic(err) }\n  fmt.Println(string(data))\n}"},{"lang":"php","label":"PHP (HTTP)","source":"<?php\n$request = curl_init(\"https://api.revkeen.com/v2/invoices/11111111-1111-4111-8111-111111111111/pay\");\ncurl_setopt_array($request, [\n  CURLOPT_CUSTOMREQUEST => \"POST\",\n  CURLOPT_RETURNTRANSFER => true,\n  CURLOPT_TIMEOUT => 10,\n  CURLOPT_HTTPHEADER => [\"x-api-key: \" . getenv(\"REVKEEN_API_KEY\"), \"Content-Type: application/json\"],\n  CURLOPT_POSTFIELDS => '{\\\"mode\\\":\\\"record\\\",\\\"payment_method\\\":\\\"bank_transfer\\\",\\\"reference\\\":\\\"CHK-12345\\\",\\\"amount_minor\\\":10000}',\n]);\n$response = curl_exec($request);\nif ($response === false) throw new RuntimeException(curl_error($request));\n$status = curl_getinfo($request, CURLINFO_HTTP_CODE);\ncurl_close($request);\nif ($status >= 400) throw new RuntimeException(\"RevKeen request failed ($status)\");\necho $response;"},{"lang":"python","label":"Python","source":"import os\n\nimport requests\n\nresponse = requests.post(\n    \"https://api.revkeen.com/v2/invoices/11111111-1111-4111-8111-111111111111/pay\",\n    headers={\"x-api-key\": os.environ[\"REVKEEN_API_KEY\"]},\n    json={\n        \"mode\": \"record\",\n        \"payment_method\": \"bank_transfer\",\n        \"reference\": \"CHK-12345\",\n        \"amount_minor\": 10000,\n    },\n)\n\nresponse.raise_for_status()\nprint(response.json())"},{"lang":"javascript","label":"JavaScript","source":"const credential = process.env.REVKEEN_API_KEY;\nif (!credential) throw new Error(\"Set REVKEEN_API_KEY\");\nconst response = await fetch(\"https://api.revkeen.com/v2/invoices/11111111-1111-4111-8111-111111111111/pay\", {\n  method: \"POST\",\n  headers: {\n    \"x-api-key\": credential,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n    \"mode\": \"record\",\n    \"payment_method\": \"bank_transfer\",\n    \"reference\": \"CHK-12345\",\n    \"amount_minor\": 10000\n  }),\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"java","label":"Java","source":"import java.net.URI;\nimport java.net.http.HttpClient;\nimport java.net.http.HttpRequest;\nimport java.net.http.HttpResponse;\n\nHttpClient client = HttpClient.newHttpClient();\n\nHttpRequest request = HttpRequest.newBuilder()\n    .uri(URI.create(\"https://api.revkeen.com/v2/invoices/11111111-1111-4111-8111-111111111111/pay\"))\n    .header(\"x-api-key\", System.getenv(\"REVKEEN_API_KEY\"))\n    .header(\"Content-Type\", \"application/json\")\n    .method(\"POST\", HttpRequest.BodyPublishers.ofString(\"\"\"\n        {\n          \"mode\": \"record\",\n          \"payment_method\": \"bank_transfer\",\n          \"reference\": \"CHK-12345\",\n          \"amount_minor\": 10000\n        }\n        \"\"\"))\n    .build();\n\nHttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());\nSystem.out.println(response.body());"},{"lang":"ruby","label":"Ruby","source":"require \"net/http\"\nrequire \"json\"\nrequire \"uri\"\n\nuri = URI(\"https://api.revkeen.com/v2/invoices/11111111-1111-4111-8111-111111111111/pay\")\nhttp = Net::HTTP.new(uri.host, uri.port)\nhttp.use_ssl = true\n\nrequest = Net::HTTP::Post.new(uri)\nrequest[\"x-api-key\"] = ENV[\"REVKEEN_API_KEY\"]\nrequest[\"Content-Type\"] = \"application/json\"\nrequest.body = {\n  \"mode\" => \"record\",\n  \"payment_method\" => \"bank_transfer\",\n  \"reference\" => \"CHK-12345\",\n  \"amount_minor\" => 10000,\n}.to_json\n\nresponse = http.request(request)\nputs response.body"}]}},"/products":{"get":{"operationId":"products_list","tags":["Products"],"x-fern-sdk-group-name":["products"],"summary":"List products","description":"List products with pagination and optional search.\n\n---\n\n**Related endpoints**\n\n- `POST /products` — Create product\n- `GET /products/{id}` — Get product by ID\n- `PATCH /products/{id}` — Update product\n- `GET /product-collections` — List product collections\n- `POST /product-collections` — Create a product collection\n- `GET /product-collections/{id}` — Get a product collection\n- `PATCH /product-collections/{id}` — Update a product collection\n- `DELETE /product-collections/{id}` — Delete a product collection\n\n**Common errors**\n\n- `400 invalid_request` — malformed payload or failed validation.\n- `401 authentication_error` — missing, invalid, expired, or revoked credential. Codes: `authentication_failed`, `invalid_api_key`, `expired_api_key`, `api_key_revoked`, `session_invalid`, `merchant_required`. Carries a `WWW-Authenticate: Bearer` challenge.\n\n**Pagination**\n\nOffset-based with `limit` (default 25, max 100) and `offset`. The response `pagination` block includes `total` and `hasMore`. See [the pagination guide](/docs/fundamentals/pagination) for SDK auto-paging helpers.","parameters":[{"schema":{"type":"integer","minimum":1,"default":1,"description":"Page number"},"required":false,"description":"Page number","name":"page","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":100,"default":20,"description":"Maximum number of results (1-100)"},"required":false,"description":"Maximum number of results (1-100)","name":"limit","in":"query"},{"schema":{"type":"string","description":"Search term to filter products"},"required":false,"description":"Search term to filter products","name":"search","in":"query"}],"responses":{"200":{"description":"List of products","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProductListResponse"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProductErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}},"headers":{"WWW-Authenticate":{"description":"RFC 6750 Bearer authentication challenge","schema":{"type":"string"},"example":"Bearer realm=\"revkeen-api\""}}},"429":{"description":"Too Many Requests - Rate limit exceeded","headers":{"Retry-After":{"description":"Whole seconds until the caller may retry","schema":{"type":"string"},"example":"60"},"X-RateLimit-Limit":{"description":"Request quota for the current window","schema":{"type":"string"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window","schema":{"type":"string"}},"X-RateLimit-Reset":{"description":"UNIX timestamp in seconds when the window resets","schema":{"type":"string"}}}}},"x-cli-group":"products","x-cli-name":"list","x-codeSamples":[{"lang":"curl","label":"cURL","source":"curl -X GET \"https://api.revkeen.com/v2/products?page=1&limit=20\" \\\n  -H \"x-api-key: $REVKEEN_API_KEY\""},{"lang":"typescript","label":"TypeScript (SDK)","source":"// npm install @revkeen/sdk@1.20260822.1541\nimport { RevKeenClient } from \"@revkeen/sdk\";\nconst apiKey = process.env.REVKEEN_API_KEY;\nif (!apiKey) throw new Error(\"Set REVKEEN_API_KEY\");\nconst client = new RevKeenClient({ apiKey, baseUrl: \"https://api.revkeen.com/v2\" });\nconst result = await client.products.list({\n  \"page\": 1,\n  \"limit\": 20\n});\nconsole.log(result);"},{"lang":"go","label":"Go (HTTP)","source":"package main\nimport (\"fmt\"; \"io\"; \"net/http\"; \"os\"; \"strings\"; \"time\")\nfunc main() {\n  req, err := http.NewRequest(\"GET\", \"https://api.revkeen.com/v2/products?page=1&limit=20\", strings.NewReader(\"\"))\n  if err != nil { panic(err) }\n  req.Header.Set(\"x-api-key\", os.Getenv(\"REVKEEN_API_KEY\"))\n  req.Header.Set(\"Content-Type\", \"application/json\")\n  client := &http.Client{Timeout: 10 * time.Second}\n  response, err := client.Do(req)\n  if err != nil { panic(err) }\n  defer response.Body.Close()\n  if response.StatusCode >= 400 { panic(fmt.Sprintf(\"RevKeen request failed (%d)\", response.StatusCode)) }\n  data, err := io.ReadAll(response.Body)\n  if err != nil { panic(err) }\n  fmt.Println(string(data))\n}"},{"lang":"php","label":"PHP (HTTP)","source":"<?php\n$request = curl_init(\"https://api.revkeen.com/v2/products?page=1&limit=20\");\ncurl_setopt_array($request, [\n  CURLOPT_CUSTOMREQUEST => \"GET\",\n  CURLOPT_RETURNTRANSFER => true,\n  CURLOPT_TIMEOUT => 10,\n  CURLOPT_HTTPHEADER => [\"x-api-key: \" . getenv(\"REVKEEN_API_KEY\"), \"Content-Type: application/json\"],\n]);\n$response = curl_exec($request);\nif ($response === false) throw new RuntimeException(curl_error($request));\n$status = curl_getinfo($request, CURLINFO_HTTP_CODE);\ncurl_close($request);\nif ($status >= 400) throw new RuntimeException(\"RevKeen request failed ($status)\");\necho $response;"},{"lang":"python","label":"Python","source":"import os\n\nimport requests\n\nresponse = requests.get(\n    \"https://api.revkeen.com/v2/products\",\n    headers={\"x-api-key\": os.environ[\"REVKEEN_API_KEY\"]},\n    params={\n        \"page\": 1,\n        \"limit\": 20,\n    },\n)\n\nresponse.raise_for_status()\nprint(response.json())"},{"lang":"javascript","label":"JavaScript","source":"const credential = process.env.REVKEEN_API_KEY;\nif (!credential) throw new Error(\"Set REVKEEN_API_KEY\");\nconst response = await fetch(\"https://api.revkeen.com/v2/products?page=1&limit=20\", {\n  method: \"GET\",\n  headers: {\n    \"x-api-key\": credential,\n  },\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"java","label":"Java","source":"import java.net.URI;\nimport java.net.http.HttpClient;\nimport java.net.http.HttpRequest;\nimport java.net.http.HttpResponse;\n\nHttpClient client = HttpClient.newHttpClient();\n\nHttpRequest request = HttpRequest.newBuilder()\n    .uri(URI.create(\"https://api.revkeen.com/v2/products?page=1&limit=20\"))\n    .header(\"x-api-key\", System.getenv(\"REVKEEN_API_KEY\"))\n    .GET()\n    .build();\n\nHttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());\nSystem.out.println(response.body());"},{"lang":"ruby","label":"Ruby","source":"require \"net/http\"\nrequire \"json\"\nrequire \"uri\"\n\nuri = URI(\"https://api.revkeen.com/v2/products?page=1&limit=20\")\nhttp = Net::HTTP.new(uri.host, uri.port)\nhttp.use_ssl = true\n\nrequest = Net::HTTP::Get.new(uri)\nrequest[\"x-api-key\"] = ENV[\"REVKEEN_API_KEY\"]\n\nresponse = http.request(request)\nputs response.body"}],"security":[{"apiKey":["products:read"]},{"oauth2":["products:read"]}]},"post":{"operationId":"products_create","tags":["Products"],"x-fern-sdk-group-name":["products"],"summary":"Create product","description":"Create a new product. Requires 'products:write' scope.\n\n---\n\n**Related endpoints**\n\n- `GET /products` — List products\n- `GET /products/{id}` — Get product by ID\n- `PATCH /products/{id}` — Update product\n- `GET /product-collections` — List product collections\n- `POST /product-collections` — Create a product collection\n- `GET /product-collections/{id}` — Get a product collection\n- `PATCH /product-collections/{id}` — Update a product collection\n- `DELETE /product-collections/{id}` — Delete a product collection\n\n**Common errors**\n\n- `400 invalid_request` — malformed payload or failed validation.\n- `401 authentication_error` — missing, invalid, expired, or revoked credential. Codes: `authentication_failed`, `invalid_api_key`, `expired_api_key`, `api_key_revoked`, `session_invalid`, `merchant_required`. Carries a `WWW-Authenticate: Bearer` challenge.\n\n**Idempotency**\n\nPass an `Idempotency-Key` header (UUID v4 recommended) to make retries safe. Keys are valid for 24 hours; see [the idempotency guide](/docs/fundamentals/idempotency).","requestBody":{"description":"Product creation details","required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateProductRequest"}}}},"responses":{"201":{"description":"Product created successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProductCreateResponse"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}},"headers":{"WWW-Authenticate":{"description":"RFC 6750 Bearer authentication challenge","schema":{"type":"string"},"example":"Bearer realm=\"revkeen-api\""}}},"429":{"description":"Too Many Requests - Rate limit exceeded","headers":{"Retry-After":{"description":"Whole seconds until the caller may retry","schema":{"type":"string"},"example":"60"},"X-RateLimit-Limit":{"description":"Request quota for the current window","schema":{"type":"string"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window","schema":{"type":"string"}},"X-RateLimit-Reset":{"description":"UNIX timestamp in seconds when the window resets","schema":{"type":"string"}}}}},"x-cli-group":"products","x-cli-name":"create","x-codeSamples":[{"lang":"curl","label":"cURL","source":"curl -X POST \"https://api.revkeen.com/v2/products\" \\\n  -H \"x-api-key: $REVKEEN_API_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"product_id\": \"prd_1RxKm4MnBoCpDqErFsGtHuIv\",\n    \"name\": \"example\",\n    \"kind\": \"subscription\",\n    \"pricing_model\": \"one_time\",\n    \"amount_minor\": 1000\n  }'"},{"lang":"typescript","label":"TypeScript (SDK)","source":"// npm install @revkeen/sdk@1.20260822.1541\nimport { RevKeenClient } from \"@revkeen/sdk\";\nconst apiKey = process.env.REVKEEN_API_KEY;\nif (!apiKey) throw new Error(\"Set REVKEEN_API_KEY\");\nconst client = new RevKeenClient({ apiKey, baseUrl: \"https://api.revkeen.com/v2\" });\nconst result = await client.products.create({\n  \"product_id\": \"prd_1RxKm4MnBoCpDqErFsGtHuIv\",\n  \"name\": \"example\",\n  \"kind\": \"subscription\",\n  \"pricing_model\": \"one_time\",\n  \"amount_minor\": 1000\n});\nconsole.log(result);"},{"lang":"go","label":"Go (HTTP)","source":"package main\nimport (\"fmt\"; \"io\"; \"net/http\"; \"os\"; \"strings\"; \"time\")\nfunc main() {\n  req, err := http.NewRequest(\"POST\", \"https://api.revkeen.com/v2/products\", strings.NewReader(\"{\\\"product_id\\\":\\\"prd_1RxKm4MnBoCpDqErFsGtHuIv\\\",\\\"name\\\":\\\"example\\\",\\\"kind\\\":\\\"subscription\\\",\\\"pricing_model\\\":\\\"one_time\\\",\\\"amount_minor\\\":1000}\"))\n  if err != nil { panic(err) }\n  req.Header.Set(\"x-api-key\", os.Getenv(\"REVKEEN_API_KEY\"))\n  req.Header.Set(\"Content-Type\", \"application/json\")\n  client := &http.Client{Timeout: 10 * time.Second}\n  response, err := client.Do(req)\n  if err != nil { panic(err) }\n  defer response.Body.Close()\n  if response.StatusCode >= 400 { panic(fmt.Sprintf(\"RevKeen request failed (%d)\", response.StatusCode)) }\n  data, err := io.ReadAll(response.Body)\n  if err != nil { panic(err) }\n  fmt.Println(string(data))\n}"},{"lang":"php","label":"PHP (HTTP)","source":"<?php\n$request = curl_init(\"https://api.revkeen.com/v2/products\");\ncurl_setopt_array($request, [\n  CURLOPT_CUSTOMREQUEST => \"POST\",\n  CURLOPT_RETURNTRANSFER => true,\n  CURLOPT_TIMEOUT => 10,\n  CURLOPT_HTTPHEADER => [\"x-api-key: \" . getenv(\"REVKEEN_API_KEY\"), \"Content-Type: application/json\"],\n  CURLOPT_POSTFIELDS => '{\\\"product_id\\\":\\\"prd_1RxKm4MnBoCpDqErFsGtHuIv\\\",\\\"name\\\":\\\"example\\\",\\\"kind\\\":\\\"subscription\\\",\\\"pricing_model\\\":\\\"one_time\\\",\\\"amount_minor\\\":1000}',\n]);\n$response = curl_exec($request);\nif ($response === false) throw new RuntimeException(curl_error($request));\n$status = curl_getinfo($request, CURLINFO_HTTP_CODE);\ncurl_close($request);\nif ($status >= 400) throw new RuntimeException(\"RevKeen request failed ($status)\");\necho $response;"},{"lang":"python","label":"Python","source":"import os\n\nimport requests\n\nresponse = requests.post(\n    \"https://api.revkeen.com/v2/products\",\n    headers={\"x-api-key\": os.environ[\"REVKEEN_API_KEY\"]},\n    json={\n        \"product_id\": \"prd_1RxKm4MnBoCpDqErFsGtHuIv\",\n        \"name\": \"example\",\n        \"kind\": \"subscription\",\n        \"pricing_model\": \"one_time\",\n        \"amount_minor\": 1000,\n    },\n)\n\nresponse.raise_for_status()\nprint(response.json())"},{"lang":"javascript","label":"JavaScript","source":"const credential = process.env.REVKEEN_API_KEY;\nif (!credential) throw new Error(\"Set REVKEEN_API_KEY\");\nconst response = await fetch(\"https://api.revkeen.com/v2/products\", {\n  method: \"POST\",\n  headers: {\n    \"x-api-key\": credential,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n    \"product_id\": \"prd_1RxKm4MnBoCpDqErFsGtHuIv\",\n    \"name\": \"example\",\n    \"kind\": \"subscription\",\n    \"pricing_model\": \"one_time\",\n    \"amount_minor\": 1000\n  }),\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"java","label":"Java","source":"import java.net.URI;\nimport java.net.http.HttpClient;\nimport java.net.http.HttpRequest;\nimport java.net.http.HttpResponse;\n\nHttpClient client = HttpClient.newHttpClient();\n\nHttpRequest request = HttpRequest.newBuilder()\n    .uri(URI.create(\"https://api.revkeen.com/v2/products\"))\n    .header(\"x-api-key\", System.getenv(\"REVKEEN_API_KEY\"))\n    .header(\"Content-Type\", \"application/json\")\n    .method(\"POST\", HttpRequest.BodyPublishers.ofString(\"\"\"\n        {\n          \"product_id\": \"prd_1RxKm4MnBoCpDqErFsGtHuIv\",\n          \"name\": \"example\",\n          \"kind\": \"subscription\",\n          \"pricing_model\": \"one_time\",\n          \"amount_minor\": 1000\n        }\n        \"\"\"))\n    .build();\n\nHttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());\nSystem.out.println(response.body());"},{"lang":"ruby","label":"Ruby","source":"require \"net/http\"\nrequire \"json\"\nrequire \"uri\"\n\nuri = URI(\"https://api.revkeen.com/v2/products\")\nhttp = Net::HTTP.new(uri.host, uri.port)\nhttp.use_ssl = true\n\nrequest = Net::HTTP::Post.new(uri)\nrequest[\"x-api-key\"] = ENV[\"REVKEEN_API_KEY\"]\nrequest[\"Content-Type\"] = \"application/json\"\nrequest.body = {\n  \"product_id\" => \"prd_1RxKm4MnBoCpDqErFsGtHuIv\",\n  \"name\" => \"example\",\n  \"kind\" => \"subscription\",\n  \"pricing_model\" => \"one_time\",\n  \"amount_minor\" => 1000,\n}.to_json\n\nresponse = http.request(request)\nputs response.body"}],"security":[{"apiKey":["products:write"]},{"oauth2":["products:write"]}]}},"/products/{id}":{"get":{"operationId":"products_get","tags":["Products"],"x-fern-sdk-group-name":["products"],"summary":"Get product by ID","description":"Get a product by its ID. Requires 'products:read' scope.\n\n---\n\n**Related endpoints**\n\n- `GET /products` — List products\n- `POST /products` — Create product\n- `PATCH /products/{id}` — Update product\n- `GET /product-collections` — List product collections\n- `POST /product-collections` — Create a product collection\n- `GET /product-collections/{id}` — Get a product collection\n- `PATCH /product-collections/{id}` — Update a product collection\n- `DELETE /product-collections/{id}` — Delete a product collection\n\n**Common errors**\n\n- `401 authentication_error` — missing, invalid, expired, or revoked credential. Codes: `authentication_failed`, `invalid_api_key`, `expired_api_key`, `api_key_revoked`, `session_invalid`, `merchant_required`. Carries a `WWW-Authenticate: Bearer` challenge.\n- `404 resource_missing` — the referenced resource does not exist or is not visible to your key.","parameters":[{"schema":{"type":"string","format":"uuid","example":"11111111-1111-4111-8111-111111111111"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Product details","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProductRetrieveResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}},"headers":{"WWW-Authenticate":{"description":"RFC 6750 Bearer authentication challenge","schema":{"type":"string"},"example":"Bearer realm=\"revkeen-api\""}}},"404":{"description":"Product not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"429":{"description":"Too Many Requests - Rate limit exceeded","headers":{"Retry-After":{"description":"Whole seconds until the caller may retry","schema":{"type":"string"},"example":"60"},"X-RateLimit-Limit":{"description":"Request quota for the current window","schema":{"type":"string"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window","schema":{"type":"string"}},"X-RateLimit-Reset":{"description":"UNIX timestamp in seconds when the window resets","schema":{"type":"string"}}}}},"x-cli-group":"products","x-cli-name":"get","x-codeSamples":[{"lang":"curl","label":"cURL","source":"curl -X GET \"https://api.revkeen.com/v2/products/11111111-1111-4111-8111-111111111111\" \\\n  -H \"x-api-key: $REVKEEN_API_KEY\""},{"lang":"typescript","label":"TypeScript (SDK)","source":"// npm install @revkeen/sdk@1.20260822.1541\nimport { RevKeenClient } from \"@revkeen/sdk\";\nconst apiKey = process.env.REVKEEN_API_KEY;\nif (!apiKey) throw new Error(\"Set REVKEEN_API_KEY\");\nconst client = new RevKeenClient({ apiKey, baseUrl: \"https://api.revkeen.com/v2\" });\nconst result = await client.products.get(\"11111111-1111-4111-8111-111111111111\");\nconsole.log(result);"},{"lang":"go","label":"Go (HTTP)","source":"package main\nimport (\"fmt\"; \"io\"; \"net/http\"; \"os\"; \"strings\"; \"time\")\nfunc main() {\n  req, err := http.NewRequest(\"GET\", \"https://api.revkeen.com/v2/products/11111111-1111-4111-8111-111111111111\", strings.NewReader(\"\"))\n  if err != nil { panic(err) }\n  req.Header.Set(\"x-api-key\", os.Getenv(\"REVKEEN_API_KEY\"))\n  req.Header.Set(\"Content-Type\", \"application/json\")\n  client := &http.Client{Timeout: 10 * time.Second}\n  response, err := client.Do(req)\n  if err != nil { panic(err) }\n  defer response.Body.Close()\n  if response.StatusCode >= 400 { panic(fmt.Sprintf(\"RevKeen request failed (%d)\", response.StatusCode)) }\n  data, err := io.ReadAll(response.Body)\n  if err != nil { panic(err) }\n  fmt.Println(string(data))\n}"},{"lang":"php","label":"PHP (HTTP)","source":"<?php\n$request = curl_init(\"https://api.revkeen.com/v2/products/11111111-1111-4111-8111-111111111111\");\ncurl_setopt_array($request, [\n  CURLOPT_CUSTOMREQUEST => \"GET\",\n  CURLOPT_RETURNTRANSFER => true,\n  CURLOPT_TIMEOUT => 10,\n  CURLOPT_HTTPHEADER => [\"x-api-key: \" . getenv(\"REVKEEN_API_KEY\"), \"Content-Type: application/json\"],\n]);\n$response = curl_exec($request);\nif ($response === false) throw new RuntimeException(curl_error($request));\n$status = curl_getinfo($request, CURLINFO_HTTP_CODE);\ncurl_close($request);\nif ($status >= 400) throw new RuntimeException(\"RevKeen request failed ($status)\");\necho $response;"},{"lang":"python","label":"Python","source":"import os\n\nimport requests\n\nresponse = requests.get(\n    \"https://api.revkeen.com/v2/products/11111111-1111-4111-8111-111111111111\",\n    headers={\"x-api-key\": os.environ[\"REVKEEN_API_KEY\"]},\n)\n\nresponse.raise_for_status()\nprint(response.json())"},{"lang":"javascript","label":"JavaScript","source":"const credential = process.env.REVKEEN_API_KEY;\nif (!credential) throw new Error(\"Set REVKEEN_API_KEY\");\nconst response = await fetch(\"https://api.revkeen.com/v2/products/11111111-1111-4111-8111-111111111111\", {\n  method: \"GET\",\n  headers: {\n    \"x-api-key\": credential,\n  },\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"java","label":"Java","source":"import java.net.URI;\nimport java.net.http.HttpClient;\nimport java.net.http.HttpRequest;\nimport java.net.http.HttpResponse;\n\nHttpClient client = HttpClient.newHttpClient();\n\nHttpRequest request = HttpRequest.newBuilder()\n    .uri(URI.create(\"https://api.revkeen.com/v2/products/11111111-1111-4111-8111-111111111111\"))\n    .header(\"x-api-key\", System.getenv(\"REVKEEN_API_KEY\"))\n    .GET()\n    .build();\n\nHttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());\nSystem.out.println(response.body());"},{"lang":"ruby","label":"Ruby","source":"require \"net/http\"\nrequire \"json\"\nrequire \"uri\"\n\nuri = URI(\"https://api.revkeen.com/v2/products/11111111-1111-4111-8111-111111111111\")\nhttp = Net::HTTP.new(uri.host, uri.port)\nhttp.use_ssl = true\n\nrequest = Net::HTTP::Get.new(uri)\nrequest[\"x-api-key\"] = ENV[\"REVKEEN_API_KEY\"]\n\nresponse = http.request(request)\nputs response.body"}],"security":[{"apiKey":["products:read"]},{"oauth2":["products:read"]}]},"patch":{"operationId":"products_update","tags":["Products"],"x-fern-sdk-group-name":["products"],"summary":"Update product","description":"Update an existing product. Requires 'products:write' scope.\n\n---\n\n**Related endpoints**\n\n- `GET /products` — List products\n- `POST /products` — Create product\n- `GET /products/{id}` — Get product by ID\n- `GET /product-collections` — List product collections\n- `POST /product-collections` — Create a product collection\n- `GET /product-collections/{id}` — Get a product collection\n- `PATCH /product-collections/{id}` — Update a product collection\n- `DELETE /product-collections/{id}` — Delete a product collection\n\n**Common errors**\n\n- `400 invalid_request` — malformed payload or failed validation.\n- `401 authentication_error` — missing, invalid, expired, or revoked credential. Codes: `authentication_failed`, `invalid_api_key`, `expired_api_key`, `api_key_revoked`, `session_invalid`, `merchant_required`. Carries a `WWW-Authenticate: Bearer` challenge.\n- `404 resource_missing` — the referenced resource does not exist or is not visible to your key.\n\n**Idempotency**\n\nPass an `Idempotency-Key` header (UUID v4 recommended) to make retries safe. Keys are valid for 24 hours; see [the idempotency guide](/docs/fundamentals/idempotency).","parameters":[{"schema":{"type":"string","format":"uuid","example":"11111111-1111-4111-8111-111111111111"},"required":true,"name":"id","in":"path"}],"requestBody":{"description":"Product update details","required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateProductRequest"}}}},"responses":{"200":{"description":"Product updated successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProductUpdateResponse"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}},"headers":{"WWW-Authenticate":{"description":"RFC 6750 Bearer authentication challenge","schema":{"type":"string"},"example":"Bearer realm=\"revkeen-api\""}}},"404":{"description":"Product not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"429":{"description":"Too Many Requests - Rate limit exceeded","headers":{"Retry-After":{"description":"Whole seconds until the caller may retry","schema":{"type":"string"},"example":"60"},"X-RateLimit-Limit":{"description":"Request quota for the current window","schema":{"type":"string"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window","schema":{"type":"string"}},"X-RateLimit-Reset":{"description":"UNIX timestamp in seconds when the window resets","schema":{"type":"string"}}}}},"x-cli-group":"products","x-cli-name":"update","x-codeSamples":[{"lang":"curl","label":"cURL","source":"curl -X PATCH \"https://api.revkeen.com/v2/products/11111111-1111-4111-8111-111111111111\" \\\n  -H \"x-api-key: $REVKEEN_API_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"name\": \"example\",\n    \"description\": \"Example request from API docs\",\n    \"amount_minor\": 1000,\n    \"currency\": \"USD\"\n  }'"},{"lang":"typescript","label":"TypeScript (SDK)","source":"// npm install @revkeen/sdk@1.20260822.1541\nimport { RevKeenClient } from \"@revkeen/sdk\";\nconst apiKey = process.env.REVKEEN_API_KEY;\nif (!apiKey) throw new Error(\"Set REVKEEN_API_KEY\");\nconst client = new RevKeenClient({ apiKey, baseUrl: \"https://api.revkeen.com/v2\" });\nconst result = await client.products.update(\"11111111-1111-4111-8111-111111111111\", {\n  \"name\": \"example\",\n  \"description\": \"Example request from API docs\",\n  \"amount_minor\": 1000,\n  \"currency\": \"USD\"\n});\nconsole.log(result);"},{"lang":"go","label":"Go (HTTP)","source":"package main\nimport (\"fmt\"; \"io\"; \"net/http\"; \"os\"; \"strings\"; \"time\")\nfunc main() {\n  req, err := http.NewRequest(\"PATCH\", \"https://api.revkeen.com/v2/products/11111111-1111-4111-8111-111111111111\", strings.NewReader(\"{\\\"name\\\":\\\"example\\\",\\\"description\\\":\\\"Example request from API docs\\\",\\\"amount_minor\\\":1000,\\\"currency\\\":\\\"USD\\\"}\"))\n  if err != nil { panic(err) }\n  req.Header.Set(\"x-api-key\", os.Getenv(\"REVKEEN_API_KEY\"))\n  req.Header.Set(\"Content-Type\", \"application/json\")\n  client := &http.Client{Timeout: 10 * time.Second}\n  response, err := client.Do(req)\n  if err != nil { panic(err) }\n  defer response.Body.Close()\n  if response.StatusCode >= 400 { panic(fmt.Sprintf(\"RevKeen request failed (%d)\", response.StatusCode)) }\n  data, err := io.ReadAll(response.Body)\n  if err != nil { panic(err) }\n  fmt.Println(string(data))\n}"},{"lang":"php","label":"PHP (HTTP)","source":"<?php\n$request = curl_init(\"https://api.revkeen.com/v2/products/11111111-1111-4111-8111-111111111111\");\ncurl_setopt_array($request, [\n  CURLOPT_CUSTOMREQUEST => \"PATCH\",\n  CURLOPT_RETURNTRANSFER => true,\n  CURLOPT_TIMEOUT => 10,\n  CURLOPT_HTTPHEADER => [\"x-api-key: \" . getenv(\"REVKEEN_API_KEY\"), \"Content-Type: application/json\"],\n  CURLOPT_POSTFIELDS => '{\\\"name\\\":\\\"example\\\",\\\"description\\\":\\\"Example request from API docs\\\",\\\"amount_minor\\\":1000,\\\"currency\\\":\\\"USD\\\"}',\n]);\n$response = curl_exec($request);\nif ($response === false) throw new RuntimeException(curl_error($request));\n$status = curl_getinfo($request, CURLINFO_HTTP_CODE);\ncurl_close($request);\nif ($status >= 400) throw new RuntimeException(\"RevKeen request failed ($status)\");\necho $response;"},{"lang":"python","label":"Python","source":"import os\n\nimport requests\n\nresponse = requests.patch(\n    \"https://api.revkeen.com/v2/products/11111111-1111-4111-8111-111111111111\",\n    headers={\"x-api-key\": os.environ[\"REVKEEN_API_KEY\"]},\n    json={\n        \"name\": \"example\",\n        \"description\": \"Example request from API docs\",\n        \"amount_minor\": 1000,\n        \"currency\": \"USD\",\n    },\n)\n\nresponse.raise_for_status()\nprint(response.json())"},{"lang":"javascript","label":"JavaScript","source":"const credential = process.env.REVKEEN_API_KEY;\nif (!credential) throw new Error(\"Set REVKEEN_API_KEY\");\nconst response = await fetch(\"https://api.revkeen.com/v2/products/11111111-1111-4111-8111-111111111111\", {\n  method: \"PATCH\",\n  headers: {\n    \"x-api-key\": credential,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n    \"name\": \"example\",\n    \"description\": \"Example request from API docs\",\n    \"amount_minor\": 1000,\n    \"currency\": \"USD\"\n  }),\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"java","label":"Java","source":"import java.net.URI;\nimport java.net.http.HttpClient;\nimport java.net.http.HttpRequest;\nimport java.net.http.HttpResponse;\n\nHttpClient client = HttpClient.newHttpClient();\n\nHttpRequest request = HttpRequest.newBuilder()\n    .uri(URI.create(\"https://api.revkeen.com/v2/products/11111111-1111-4111-8111-111111111111\"))\n    .header(\"x-api-key\", System.getenv(\"REVKEEN_API_KEY\"))\n    .header(\"Content-Type\", \"application/json\")\n    .method(\"PATCH\", HttpRequest.BodyPublishers.ofString(\"\"\"\n        {\n          \"name\": \"example\",\n          \"description\": \"Example request from API docs\",\n          \"amount_minor\": 1000,\n          \"currency\": \"USD\"\n        }\n        \"\"\"))\n    .build();\n\nHttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());\nSystem.out.println(response.body());"},{"lang":"ruby","label":"Ruby","source":"require \"net/http\"\nrequire \"json\"\nrequire \"uri\"\n\nuri = URI(\"https://api.revkeen.com/v2/products/11111111-1111-4111-8111-111111111111\")\nhttp = Net::HTTP.new(uri.host, uri.port)\nhttp.use_ssl = true\n\nrequest = Net::HTTP::Patch.new(uri)\nrequest[\"x-api-key\"] = ENV[\"REVKEEN_API_KEY\"]\nrequest[\"Content-Type\"] = \"application/json\"\nrequest.body = {\n  \"name\" => \"example\",\n  \"description\" => \"Example request from API docs\",\n  \"amount_minor\" => 1000,\n  \"currency\" => \"USD\",\n}.to_json\n\nresponse = http.request(request)\nputs response.body"}],"security":[{"apiKey":["products:write"]},{"oauth2":["products:write"]}]}},"/product-collections":{"get":{"operationId":"product_collections_list","tags":["Products"],"x-fern-sdk-group-name":["product_collections"],"summary":"List product collections","description":"List the merchant's product collections, with member product ids in display order.\n\n**Pagination**\n\nOffset-based with `limit` (default 20, max 100) and `offset`. The response includes `has_more`, and echoes `limit` and `offset`.\n\n---\n\n**Related endpoints**\n\n- `GET /products` — List products\n- `POST /products` — Create product\n- `GET /products/{id}` — Get product by ID\n- `PATCH /products/{id}` — Update product\n- `POST /product-collections` — Create a product collection\n- `GET /product-collections/{id}` — Get a product collection\n- `PATCH /product-collections/{id}` — Update a product collection\n- `DELETE /product-collections/{id}` — Delete a product collection\n\n**Common errors**\n\n- `403 authorization_error` — the caller is identified but the action is denied. Codes include `insufficient_permissions`, `ip_not_allowed`, `merchant_mismatch`, and `origin_not_allowed`.","parameters":[{"schema":{"type":"integer","minimum":1,"maximum":100,"default":20,"description":"Maximum collections to return (1-100)"},"required":false,"description":"Maximum collections to return (1-100)","name":"limit","in":"query"},{"schema":{"type":["integer","null"],"minimum":0,"default":0,"description":"Collections to skip"},"required":false,"description":"Collections to skip","name":"offset","in":"query"}],"responses":{"200":{"description":"Product collections","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProductCollectionListResponse"}}}},"401":{"description":"Unauthorized - Invalid or missing API key","headers":{"WWW-Authenticate":{"description":"RFC 6750 Bearer authentication challenge","schema":{"type":"string"},"example":"Bearer realm=\"revkeen-api\""}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProductCollectionError"}}}},"429":{"description":"Too Many Requests - Rate limit exceeded","headers":{"Retry-After":{"description":"Whole seconds until the caller may retry","schema":{"type":"string"},"example":"60"},"X-RateLimit-Limit":{"description":"Request quota for the current window","schema":{"type":"string"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window","schema":{"type":"string"}},"X-RateLimit-Reset":{"description":"UNIX timestamp in seconds when the window resets","schema":{"type":"string"}}}}},"x-cli-group":"product-collections","x-cli-name":"list","x-codeSamples":[{"lang":"curl","label":"cURL","source":"curl -X GET \"https://api.revkeen.com/v2/product-collections?limit=20&offset=0\" \\\n  -H \"x-api-key: $REVKEEN_API_KEY\""},{"lang":"typescript","label":"TypeScript (HTTP)","source":"const credential = process.env.REVKEEN_API_KEY;\nif (!credential) throw new Error(\"Set REVKEEN_API_KEY\");\nconst response = await fetch(\"https://api.revkeen.com/v2/product-collections?limit=20&offset=0\", {\n  method: \"GET\",\n  headers: {\n    \"x-api-key\": credential,\n  },\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"go","label":"Go (HTTP)","source":"package main\nimport (\"fmt\"; \"io\"; \"net/http\"; \"os\"; \"strings\"; \"time\")\nfunc main() {\n  req, err := http.NewRequest(\"GET\", \"https://api.revkeen.com/v2/product-collections?limit=20&offset=0\", strings.NewReader(\"\"))\n  if err != nil { panic(err) }\n  req.Header.Set(\"x-api-key\", os.Getenv(\"REVKEEN_API_KEY\"))\n  req.Header.Set(\"Content-Type\", \"application/json\")\n  client := &http.Client{Timeout: 10 * time.Second}\n  response, err := client.Do(req)\n  if err != nil { panic(err) }\n  defer response.Body.Close()\n  if response.StatusCode >= 400 { panic(fmt.Sprintf(\"RevKeen request failed (%d)\", response.StatusCode)) }\n  data, err := io.ReadAll(response.Body)\n  if err != nil { panic(err) }\n  fmt.Println(string(data))\n}"},{"lang":"php","label":"PHP (HTTP)","source":"<?php\n$request = curl_init(\"https://api.revkeen.com/v2/product-collections?limit=20&offset=0\");\ncurl_setopt_array($request, [\n  CURLOPT_CUSTOMREQUEST => \"GET\",\n  CURLOPT_RETURNTRANSFER => true,\n  CURLOPT_TIMEOUT => 10,\n  CURLOPT_HTTPHEADER => [\"x-api-key: \" . getenv(\"REVKEEN_API_KEY\"), \"Content-Type: application/json\"],\n]);\n$response = curl_exec($request);\nif ($response === false) throw new RuntimeException(curl_error($request));\n$status = curl_getinfo($request, CURLINFO_HTTP_CODE);\ncurl_close($request);\nif ($status >= 400) throw new RuntimeException(\"RevKeen request failed ($status)\");\necho $response;"},{"lang":"python","label":"Python","source":"import os\n\nimport requests\n\nresponse = requests.get(\n    \"https://api.revkeen.com/v2/product-collections\",\n    headers={\"x-api-key\": os.environ[\"REVKEEN_API_KEY\"]},\n    params={\n        \"limit\": 20,\n        \"offset\": 0,\n    },\n)\n\nresponse.raise_for_status()\nprint(response.json())"},{"lang":"javascript","label":"JavaScript","source":"const credential = process.env.REVKEEN_API_KEY;\nif (!credential) throw new Error(\"Set REVKEEN_API_KEY\");\nconst response = await fetch(\"https://api.revkeen.com/v2/product-collections?limit=20&offset=0\", {\n  method: \"GET\",\n  headers: {\n    \"x-api-key\": credential,\n  },\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"java","label":"Java","source":"import java.net.URI;\nimport java.net.http.HttpClient;\nimport java.net.http.HttpRequest;\nimport java.net.http.HttpResponse;\n\nHttpClient client = HttpClient.newHttpClient();\n\nHttpRequest request = HttpRequest.newBuilder()\n    .uri(URI.create(\"https://api.revkeen.com/v2/product-collections?limit=20&offset=0\"))\n    .header(\"x-api-key\", System.getenv(\"REVKEEN_API_KEY\"))\n    .GET()\n    .build();\n\nHttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());\nSystem.out.println(response.body());"},{"lang":"ruby","label":"Ruby","source":"require \"net/http\"\nrequire \"json\"\nrequire \"uri\"\n\nuri = URI(\"https://api.revkeen.com/v2/product-collections?limit=20&offset=0\")\nhttp = Net::HTTP.new(uri.host, uri.port)\nhttp.use_ssl = true\n\nrequest = Net::HTTP::Get.new(uri)\nrequest[\"x-api-key\"] = ENV[\"REVKEEN_API_KEY\"]\n\nresponse = http.request(request)\nputs response.body"}],"security":[{"apiKey":["products:read"]},{"oauth2":["products:read"]}]},"post":{"operationId":"product_collections_create","tags":["Products"],"x-fern-sdk-group-name":["product_collections"],"summary":"Create a product collection","description":"Create a product collection. The URL handle must be unique for the merchant, and every product must belong to the merchant.\n\n---\n\n**Related endpoints**\n\n- `GET /products` — List products\n- `POST /products` — Create product\n- `GET /products/{id}` — Get product by ID\n- `PATCH /products/{id}` — Update product\n- `GET /product-collections` — List product collections\n- `GET /product-collections/{id}` — Get a product collection\n- `PATCH /product-collections/{id}` — Update a product collection\n- `DELETE /product-collections/{id}` — Delete a product collection\n\n**Common errors**\n\n- `403 authorization_error` — the caller is identified but the action is denied. Codes include `insufficient_permissions`, `ip_not_allowed`, `merchant_mismatch`, and `origin_not_allowed`.\n- `409 conflict` — Idempotency-Key collision with a different body, or a concurrent state-transition conflict.\n- `422 unprocessable_entity` — business-rule failure (for example, refunding more than the original charge).\n\n**Idempotency**\n\nPass an `Idempotency-Key` header (UUID v4 recommended) to make retries safe. Keys are valid for 24 hours; see [the idempotency guide](/docs/fundamentals/idempotency).","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProductCollectionCreateRequest"}}}},"responses":{"201":{"description":"Created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProductCollectionCreateResponse"}}}},"401":{"description":"Unauthorized - Invalid or missing API key","headers":{"WWW-Authenticate":{"description":"RFC 6750 Bearer authentication challenge","schema":{"type":"string"},"example":"Bearer realm=\"revkeen-api\""}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProductCollectionError"}}}},"409":{"description":"URL handle already used","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProductCollectionError"}}}},"422":{"description":"Unknown product ids","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProductCollectionError"}}}},"429":{"description":"Too Many Requests - Rate limit exceeded","headers":{"Retry-After":{"description":"Whole seconds until the caller may retry","schema":{"type":"string"},"example":"60"},"X-RateLimit-Limit":{"description":"Request quota for the current window","schema":{"type":"string"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window","schema":{"type":"string"}},"X-RateLimit-Reset":{"description":"UNIX timestamp in seconds when the window resets","schema":{"type":"string"}}}}},"x-cli-group":"product-collections","x-cli-name":"create","x-codeSamples":[{"lang":"curl","label":"cURL","source":"curl -X POST \"https://api.revkeen.com/v2/product-collections\" \\\n  -H \"x-api-key: $REVKEEN_API_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"name\": \"example\",\n    \"slug\": \"example\"\n  }'"},{"lang":"typescript","label":"TypeScript (HTTP)","source":"const credential = process.env.REVKEEN_API_KEY;\nif (!credential) throw new Error(\"Set REVKEEN_API_KEY\");\nconst response = await fetch(\"https://api.revkeen.com/v2/product-collections\", {\n  method: \"POST\",\n  headers: {\n    \"x-api-key\": credential,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n    \"name\": \"example\",\n    \"slug\": \"example\"\n  }),\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"go","label":"Go (HTTP)","source":"package main\nimport (\"fmt\"; \"io\"; \"net/http\"; \"os\"; \"strings\"; \"time\")\nfunc main() {\n  req, err := http.NewRequest(\"POST\", \"https://api.revkeen.com/v2/product-collections\", strings.NewReader(\"{\\\"name\\\":\\\"example\\\",\\\"slug\\\":\\\"example\\\"}\"))\n  if err != nil { panic(err) }\n  req.Header.Set(\"x-api-key\", os.Getenv(\"REVKEEN_API_KEY\"))\n  req.Header.Set(\"Content-Type\", \"application/json\")\n  client := &http.Client{Timeout: 10 * time.Second}\n  response, err := client.Do(req)\n  if err != nil { panic(err) }\n  defer response.Body.Close()\n  if response.StatusCode >= 400 { panic(fmt.Sprintf(\"RevKeen request failed (%d)\", response.StatusCode)) }\n  data, err := io.ReadAll(response.Body)\n  if err != nil { panic(err) }\n  fmt.Println(string(data))\n}"},{"lang":"php","label":"PHP (HTTP)","source":"<?php\n$request = curl_init(\"https://api.revkeen.com/v2/product-collections\");\ncurl_setopt_array($request, [\n  CURLOPT_CUSTOMREQUEST => \"POST\",\n  CURLOPT_RETURNTRANSFER => true,\n  CURLOPT_TIMEOUT => 10,\n  CURLOPT_HTTPHEADER => [\"x-api-key: \" . getenv(\"REVKEEN_API_KEY\"), \"Content-Type: application/json\"],\n  CURLOPT_POSTFIELDS => '{\\\"name\\\":\\\"example\\\",\\\"slug\\\":\\\"example\\\"}',\n]);\n$response = curl_exec($request);\nif ($response === false) throw new RuntimeException(curl_error($request));\n$status = curl_getinfo($request, CURLINFO_HTTP_CODE);\ncurl_close($request);\nif ($status >= 400) throw new RuntimeException(\"RevKeen request failed ($status)\");\necho $response;"},{"lang":"python","label":"Python","source":"import os\n\nimport requests\n\nresponse = requests.post(\n    \"https://api.revkeen.com/v2/product-collections\",\n    headers={\"x-api-key\": os.environ[\"REVKEEN_API_KEY\"]},\n    json={\n        \"name\": \"example\",\n        \"slug\": \"example\",\n    },\n)\n\nresponse.raise_for_status()\nprint(response.json())"},{"lang":"javascript","label":"JavaScript","source":"const credential = process.env.REVKEEN_API_KEY;\nif (!credential) throw new Error(\"Set REVKEEN_API_KEY\");\nconst response = await fetch(\"https://api.revkeen.com/v2/product-collections\", {\n  method: \"POST\",\n  headers: {\n    \"x-api-key\": credential,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n    \"name\": \"example\",\n    \"slug\": \"example\"\n  }),\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"java","label":"Java","source":"import java.net.URI;\nimport java.net.http.HttpClient;\nimport java.net.http.HttpRequest;\nimport java.net.http.HttpResponse;\n\nHttpClient client = HttpClient.newHttpClient();\n\nHttpRequest request = HttpRequest.newBuilder()\n    .uri(URI.create(\"https://api.revkeen.com/v2/product-collections\"))\n    .header(\"x-api-key\", System.getenv(\"REVKEEN_API_KEY\"))\n    .header(\"Content-Type\", \"application/json\")\n    .method(\"POST\", HttpRequest.BodyPublishers.ofString(\"\"\"\n        {\n          \"name\": \"example\",\n          \"slug\": \"example\"\n        }\n        \"\"\"))\n    .build();\n\nHttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());\nSystem.out.println(response.body());"},{"lang":"ruby","label":"Ruby","source":"require \"net/http\"\nrequire \"json\"\nrequire \"uri\"\n\nuri = URI(\"https://api.revkeen.com/v2/product-collections\")\nhttp = Net::HTTP.new(uri.host, uri.port)\nhttp.use_ssl = true\n\nrequest = Net::HTTP::Post.new(uri)\nrequest[\"x-api-key\"] = ENV[\"REVKEEN_API_KEY\"]\nrequest[\"Content-Type\"] = \"application/json\"\nrequest.body = {\n  \"name\" => \"example\",\n  \"slug\" => \"example\",\n}.to_json\n\nresponse = http.request(request)\nputs response.body"}],"security":[{"apiKey":["products:write"]},{"oauth2":["products:write"]}]}},"/product-collections/{id}":{"get":{"operationId":"product_collections_get","tags":["Products"],"x-fern-sdk-group-name":["product_collections"],"summary":"Get a product collection","description":"Get one product collection by id or URL handle.\n\n---\n\n**Related endpoints**\n\n- `GET /products` — List products\n- `POST /products` — Create product\n- `GET /products/{id}` — Get product by ID\n- `PATCH /products/{id}` — Update product\n- `GET /product-collections` — List product collections\n- `POST /product-collections` — Create a product collection\n- `PATCH /product-collections/{id}` — Update a product collection\n- `DELETE /product-collections/{id}` — Delete a product collection\n\n**Common errors**\n\n- `403 authorization_error` — the caller is identified but the action is denied. Codes include `insufficient_permissions`, `ip_not_allowed`, `merchant_mismatch`, and `origin_not_allowed`.\n- `404 resource_missing` — the referenced resource does not exist or is not visible to your key.","parameters":[{"schema":{"type":"string","minLength":1,"maxLength":80,"description":"Collection id or URL handle."},"required":true,"description":"Collection id or URL handle.","name":"id","in":"path"}],"responses":{"200":{"description":"Product collection","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProductCollectionResponse"}}}},"401":{"description":"Unauthorized - Invalid or missing API key","headers":{"WWW-Authenticate":{"description":"RFC 6750 Bearer authentication challenge","schema":{"type":"string"},"example":"Bearer realm=\"revkeen-api\""}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProductCollectionError"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProductCollectionError"}}}},"429":{"description":"Too Many Requests - Rate limit exceeded","headers":{"Retry-After":{"description":"Whole seconds until the caller may retry","schema":{"type":"string"},"example":"60"},"X-RateLimit-Limit":{"description":"Request quota for the current window","schema":{"type":"string"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window","schema":{"type":"string"}},"X-RateLimit-Reset":{"description":"UNIX timestamp in seconds when the window resets","schema":{"type":"string"}}}}},"x-cli-group":"product-collections","x-cli-name":"get","x-codeSamples":[{"lang":"curl","label":"cURL","source":"curl -X GET \"https://api.revkeen.com/v2/product-collections/prd_1RxKm4MnBoCpDqErFsGtHuIv\" \\\n  -H \"x-api-key: $REVKEEN_API_KEY\""},{"lang":"typescript","label":"TypeScript (HTTP)","source":"const credential = process.env.REVKEEN_API_KEY;\nif (!credential) throw new Error(\"Set REVKEEN_API_KEY\");\nconst response = await fetch(\"https://api.revkeen.com/v2/product-collections/prd_1RxKm4MnBoCpDqErFsGtHuIv\", {\n  method: \"GET\",\n  headers: {\n    \"x-api-key\": credential,\n  },\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"go","label":"Go (HTTP)","source":"package main\nimport (\"fmt\"; \"io\"; \"net/http\"; \"os\"; \"strings\"; \"time\")\nfunc main() {\n  req, err := http.NewRequest(\"GET\", \"https://api.revkeen.com/v2/product-collections/prd_1RxKm4MnBoCpDqErFsGtHuIv\", strings.NewReader(\"\"))\n  if err != nil { panic(err) }\n  req.Header.Set(\"x-api-key\", os.Getenv(\"REVKEEN_API_KEY\"))\n  req.Header.Set(\"Content-Type\", \"application/json\")\n  client := &http.Client{Timeout: 10 * time.Second}\n  response, err := client.Do(req)\n  if err != nil { panic(err) }\n  defer response.Body.Close()\n  if response.StatusCode >= 400 { panic(fmt.Sprintf(\"RevKeen request failed (%d)\", response.StatusCode)) }\n  data, err := io.ReadAll(response.Body)\n  if err != nil { panic(err) }\n  fmt.Println(string(data))\n}"},{"lang":"php","label":"PHP (HTTP)","source":"<?php\n$request = curl_init(\"https://api.revkeen.com/v2/product-collections/prd_1RxKm4MnBoCpDqErFsGtHuIv\");\ncurl_setopt_array($request, [\n  CURLOPT_CUSTOMREQUEST => \"GET\",\n  CURLOPT_RETURNTRANSFER => true,\n  CURLOPT_TIMEOUT => 10,\n  CURLOPT_HTTPHEADER => [\"x-api-key: \" . getenv(\"REVKEEN_API_KEY\"), \"Content-Type: application/json\"],\n]);\n$response = curl_exec($request);\nif ($response === false) throw new RuntimeException(curl_error($request));\n$status = curl_getinfo($request, CURLINFO_HTTP_CODE);\ncurl_close($request);\nif ($status >= 400) throw new RuntimeException(\"RevKeen request failed ($status)\");\necho $response;"},{"lang":"python","label":"Python","source":"import os\n\nimport requests\n\nresponse = requests.get(\n    \"https://api.revkeen.com/v2/product-collections/prd_1RxKm4MnBoCpDqErFsGtHuIv\",\n    headers={\"x-api-key\": os.environ[\"REVKEEN_API_KEY\"]},\n)\n\nresponse.raise_for_status()\nprint(response.json())"},{"lang":"javascript","label":"JavaScript","source":"const credential = process.env.REVKEEN_API_KEY;\nif (!credential) throw new Error(\"Set REVKEEN_API_KEY\");\nconst response = await fetch(\"https://api.revkeen.com/v2/product-collections/prd_1RxKm4MnBoCpDqErFsGtHuIv\", {\n  method: \"GET\",\n  headers: {\n    \"x-api-key\": credential,\n  },\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"java","label":"Java","source":"import java.net.URI;\nimport java.net.http.HttpClient;\nimport java.net.http.HttpRequest;\nimport java.net.http.HttpResponse;\n\nHttpClient client = HttpClient.newHttpClient();\n\nHttpRequest request = HttpRequest.newBuilder()\n    .uri(URI.create(\"https://api.revkeen.com/v2/product-collections/prd_1RxKm4MnBoCpDqErFsGtHuIv\"))\n    .header(\"x-api-key\", System.getenv(\"REVKEEN_API_KEY\"))\n    .GET()\n    .build();\n\nHttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());\nSystem.out.println(response.body());"},{"lang":"ruby","label":"Ruby","source":"require \"net/http\"\nrequire \"json\"\nrequire \"uri\"\n\nuri = URI(\"https://api.revkeen.com/v2/product-collections/prd_1RxKm4MnBoCpDqErFsGtHuIv\")\nhttp = Net::HTTP.new(uri.host, uri.port)\nhttp.use_ssl = true\n\nrequest = Net::HTTP::Get.new(uri)\nrequest[\"x-api-key\"] = ENV[\"REVKEEN_API_KEY\"]\n\nresponse = http.request(request)\nputs response.body"}],"security":[{"apiKey":["products:read"]},{"oauth2":["products:read"]}]},"patch":{"operationId":"product_collections_update","tags":["Products"],"x-fern-sdk-group-name":["product_collections"],"summary":"Update a product collection","description":"Update a product collection. `product_ids` replaces the membership in the given order. Send `expected_version` to refuse the change if the collection was edited since you read it.\n\n---\n\n**Related endpoints**\n\n- `GET /products` — List products\n- `POST /products` — Create product\n- `GET /products/{id}` — Get product by ID\n- `PATCH /products/{id}` — Update product\n- `GET /product-collections` — List product collections\n- `POST /product-collections` — Create a product collection\n- `GET /product-collections/{id}` — Get a product collection\n- `DELETE /product-collections/{id}` — Delete a product collection\n\n**Common errors**\n\n- `403 authorization_error` — the caller is identified but the action is denied. Codes include `insufficient_permissions`, `ip_not_allowed`, `merchant_mismatch`, and `origin_not_allowed`.\n- `404 resource_missing` — the referenced resource does not exist or is not visible to your key.\n- `409 conflict` — Idempotency-Key collision with a different body, or a concurrent state-transition conflict.\n- `422 unprocessable_entity` — business-rule failure (for example, refunding more than the original charge).\n\n**Idempotency**\n\nPass an `Idempotency-Key` header (UUID v4 recommended) to make retries safe. Keys are valid for 24 hours; see [the idempotency guide](/docs/fundamentals/idempotency).","parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProductCollectionUpdateRequest"}}}},"responses":{"200":{"description":"Updated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProductCollectionUpdateResponse"}}}},"401":{"description":"Unauthorized - Invalid or missing API key","headers":{"WWW-Authenticate":{"description":"RFC 6750 Bearer authentication challenge","schema":{"type":"string"},"example":"Bearer realm=\"revkeen-api\""}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProductCollectionError"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProductCollectionError"}}}},"409":{"description":"Version conflict or URL handle already used","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProductCollectionError"}}}},"422":{"description":"Unknown product ids","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProductCollectionError"}}}},"429":{"description":"Too Many Requests - Rate limit exceeded","headers":{"Retry-After":{"description":"Whole seconds until the caller may retry","schema":{"type":"string"},"example":"60"},"X-RateLimit-Limit":{"description":"Request quota for the current window","schema":{"type":"string"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window","schema":{"type":"string"}},"X-RateLimit-Reset":{"description":"UNIX timestamp in seconds when the window resets","schema":{"type":"string"}}}}},"x-cli-group":"product-collections","x-cli-name":"update","x-codeSamples":[{"lang":"curl","label":"cURL","source":"curl -X PATCH \"https://api.revkeen.com/v2/product-collections/11111111-1111-4111-8111-111111111111\" \\\n  -H \"x-api-key: $REVKEEN_API_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"name\": \"example\",\n    \"slug\": \"example\",\n    \"description\": \"Example request from API docs\",\n    \"image_asset_id\": \"11111111-1111-4111-8111-111111111111\"\n  }'"},{"lang":"typescript","label":"TypeScript (HTTP)","source":"const credential = process.env.REVKEEN_API_KEY;\nif (!credential) throw new Error(\"Set REVKEEN_API_KEY\");\nconst response = await fetch(\"https://api.revkeen.com/v2/product-collections/11111111-1111-4111-8111-111111111111\", {\n  method: \"PATCH\",\n  headers: {\n    \"x-api-key\": credential,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n    \"name\": \"example\",\n    \"slug\": \"example\",\n    \"description\": \"Example request from API docs\",\n    \"image_asset_id\": \"11111111-1111-4111-8111-111111111111\"\n  }),\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"go","label":"Go (HTTP)","source":"package main\nimport (\"fmt\"; \"io\"; \"net/http\"; \"os\"; \"strings\"; \"time\")\nfunc main() {\n  req, err := http.NewRequest(\"PATCH\", \"https://api.revkeen.com/v2/product-collections/11111111-1111-4111-8111-111111111111\", strings.NewReader(\"{\\\"name\\\":\\\"example\\\",\\\"slug\\\":\\\"example\\\",\\\"description\\\":\\\"Example request from API docs\\\",\\\"image_asset_id\\\":\\\"11111111-1111-4111-8111-111111111111\\\"}\"))\n  if err != nil { panic(err) }\n  req.Header.Set(\"x-api-key\", os.Getenv(\"REVKEEN_API_KEY\"))\n  req.Header.Set(\"Content-Type\", \"application/json\")\n  client := &http.Client{Timeout: 10 * time.Second}\n  response, err := client.Do(req)\n  if err != nil { panic(err) }\n  defer response.Body.Close()\n  if response.StatusCode >= 400 { panic(fmt.Sprintf(\"RevKeen request failed (%d)\", response.StatusCode)) }\n  data, err := io.ReadAll(response.Body)\n  if err != nil { panic(err) }\n  fmt.Println(string(data))\n}"},{"lang":"php","label":"PHP (HTTP)","source":"<?php\n$request = curl_init(\"https://api.revkeen.com/v2/product-collections/11111111-1111-4111-8111-111111111111\");\ncurl_setopt_array($request, [\n  CURLOPT_CUSTOMREQUEST => \"PATCH\",\n  CURLOPT_RETURNTRANSFER => true,\n  CURLOPT_TIMEOUT => 10,\n  CURLOPT_HTTPHEADER => [\"x-api-key: \" . getenv(\"REVKEEN_API_KEY\"), \"Content-Type: application/json\"],\n  CURLOPT_POSTFIELDS => '{\\\"name\\\":\\\"example\\\",\\\"slug\\\":\\\"example\\\",\\\"description\\\":\\\"Example request from API docs\\\",\\\"image_asset_id\\\":\\\"11111111-1111-4111-8111-111111111111\\\"}',\n]);\n$response = curl_exec($request);\nif ($response === false) throw new RuntimeException(curl_error($request));\n$status = curl_getinfo($request, CURLINFO_HTTP_CODE);\ncurl_close($request);\nif ($status >= 400) throw new RuntimeException(\"RevKeen request failed ($status)\");\necho $response;"},{"lang":"python","label":"Python","source":"import os\n\nimport requests\n\nresponse = requests.patch(\n    \"https://api.revkeen.com/v2/product-collections/11111111-1111-4111-8111-111111111111\",\n    headers={\"x-api-key\": os.environ[\"REVKEEN_API_KEY\"]},\n    json={\n        \"name\": \"example\",\n        \"slug\": \"example\",\n        \"description\": \"Example request from API docs\",\n        \"image_asset_id\": \"11111111-1111-4111-8111-111111111111\",\n    },\n)\n\nresponse.raise_for_status()\nprint(response.json())"},{"lang":"javascript","label":"JavaScript","source":"const credential = process.env.REVKEEN_API_KEY;\nif (!credential) throw new Error(\"Set REVKEEN_API_KEY\");\nconst response = await fetch(\"https://api.revkeen.com/v2/product-collections/11111111-1111-4111-8111-111111111111\", {\n  method: \"PATCH\",\n  headers: {\n    \"x-api-key\": credential,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n    \"name\": \"example\",\n    \"slug\": \"example\",\n    \"description\": \"Example request from API docs\",\n    \"image_asset_id\": \"11111111-1111-4111-8111-111111111111\"\n  }),\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"java","label":"Java","source":"import java.net.URI;\nimport java.net.http.HttpClient;\nimport java.net.http.HttpRequest;\nimport java.net.http.HttpResponse;\n\nHttpClient client = HttpClient.newHttpClient();\n\nHttpRequest request = HttpRequest.newBuilder()\n    .uri(URI.create(\"https://api.revkeen.com/v2/product-collections/11111111-1111-4111-8111-111111111111\"))\n    .header(\"x-api-key\", System.getenv(\"REVKEEN_API_KEY\"))\n    .header(\"Content-Type\", \"application/json\")\n    .method(\"PATCH\", HttpRequest.BodyPublishers.ofString(\"\"\"\n        {\n          \"name\": \"example\",\n          \"slug\": \"example\",\n          \"description\": \"Example request from API docs\",\n          \"image_asset_id\": \"11111111-1111-4111-8111-111111111111\"\n        }\n        \"\"\"))\n    .build();\n\nHttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());\nSystem.out.println(response.body());"},{"lang":"ruby","label":"Ruby","source":"require \"net/http\"\nrequire \"json\"\nrequire \"uri\"\n\nuri = URI(\"https://api.revkeen.com/v2/product-collections/11111111-1111-4111-8111-111111111111\")\nhttp = Net::HTTP.new(uri.host, uri.port)\nhttp.use_ssl = true\n\nrequest = Net::HTTP::Patch.new(uri)\nrequest[\"x-api-key\"] = ENV[\"REVKEEN_API_KEY\"]\nrequest[\"Content-Type\"] = \"application/json\"\nrequest.body = {\n  \"name\" => \"example\",\n  \"slug\" => \"example\",\n  \"description\" => \"Example request from API docs\",\n  \"image_asset_id\" => \"11111111-1111-4111-8111-111111111111\",\n}.to_json\n\nresponse = http.request(request)\nputs response.body"}],"security":[{"apiKey":["products:write"]},{"oauth2":["products:write"]}]},"delete":{"operationId":"product_collections_delete","tags":["Products"],"x-fern-sdk-group-name":["product_collections"],"summary":"Delete a product collection","description":"Delete a product collection. Its products are not changed.\n\n---\n\n**Related endpoints**\n\n- `GET /products` — List products\n- `POST /products` — Create product\n- `GET /products/{id}` — Get product by ID\n- `PATCH /products/{id}` — Update product\n- `GET /product-collections` — List product collections\n- `POST /product-collections` — Create a product collection\n- `GET /product-collections/{id}` — Get a product collection\n- `PATCH /product-collections/{id}` — Update a product collection\n\n**Common errors**\n\n- `403 authorization_error` — the caller is identified but the action is denied. Codes include `insufficient_permissions`, `ip_not_allowed`, `merchant_mismatch`, and `origin_not_allowed`.\n- `404 resource_missing` — the referenced resource does not exist or is not visible to your key.\n\n**Idempotency**\n\nPass an `Idempotency-Key` header (UUID v4 recommended) to make retries safe. Keys are valid for 24 hours; see [the idempotency guide](/docs/fundamentals/idempotency).","parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Deleted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProductCollectionDeletedResponse"}}}},"401":{"description":"Unauthorized - Invalid or missing API key","headers":{"WWW-Authenticate":{"description":"RFC 6750 Bearer authentication challenge","schema":{"type":"string"},"example":"Bearer realm=\"revkeen-api\""}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProductCollectionError"}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProductCollectionError"}}}},"429":{"description":"Too Many Requests - Rate limit exceeded","headers":{"Retry-After":{"description":"Whole seconds until the caller may retry","schema":{"type":"string"},"example":"60"},"X-RateLimit-Limit":{"description":"Request quota for the current window","schema":{"type":"string"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window","schema":{"type":"string"}},"X-RateLimit-Reset":{"description":"UNIX timestamp in seconds when the window resets","schema":{"type":"string"}}}}},"x-cli-group":"product-collections","x-cli-name":"delete","x-codeSamples":[{"lang":"curl","label":"cURL","source":"curl -X DELETE \"https://api.revkeen.com/v2/product-collections/11111111-1111-4111-8111-111111111111\" \\\n  -H \"x-api-key: $REVKEEN_API_KEY\""},{"lang":"typescript","label":"TypeScript (HTTP)","source":"const credential = process.env.REVKEEN_API_KEY;\nif (!credential) throw new Error(\"Set REVKEEN_API_KEY\");\nconst response = await fetch(\"https://api.revkeen.com/v2/product-collections/11111111-1111-4111-8111-111111111111\", {\n  method: \"DELETE\",\n  headers: {\n    \"x-api-key\": credential,\n  },\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"go","label":"Go (HTTP)","source":"package main\nimport (\"fmt\"; \"io\"; \"net/http\"; \"os\"; \"strings\"; \"time\")\nfunc main() {\n  req, err := http.NewRequest(\"DELETE\", \"https://api.revkeen.com/v2/product-collections/11111111-1111-4111-8111-111111111111\", strings.NewReader(\"\"))\n  if err != nil { panic(err) }\n  req.Header.Set(\"x-api-key\", os.Getenv(\"REVKEEN_API_KEY\"))\n  req.Header.Set(\"Content-Type\", \"application/json\")\n  client := &http.Client{Timeout: 10 * time.Second}\n  response, err := client.Do(req)\n  if err != nil { panic(err) }\n  defer response.Body.Close()\n  if response.StatusCode >= 400 { panic(fmt.Sprintf(\"RevKeen request failed (%d)\", response.StatusCode)) }\n  data, err := io.ReadAll(response.Body)\n  if err != nil { panic(err) }\n  fmt.Println(string(data))\n}"},{"lang":"php","label":"PHP (HTTP)","source":"<?php\n$request = curl_init(\"https://api.revkeen.com/v2/product-collections/11111111-1111-4111-8111-111111111111\");\ncurl_setopt_array($request, [\n  CURLOPT_CUSTOMREQUEST => \"DELETE\",\n  CURLOPT_RETURNTRANSFER => true,\n  CURLOPT_TIMEOUT => 10,\n  CURLOPT_HTTPHEADER => [\"x-api-key: \" . getenv(\"REVKEEN_API_KEY\"), \"Content-Type: application/json\"],\n]);\n$response = curl_exec($request);\nif ($response === false) throw new RuntimeException(curl_error($request));\n$status = curl_getinfo($request, CURLINFO_HTTP_CODE);\ncurl_close($request);\nif ($status >= 400) throw new RuntimeException(\"RevKeen request failed ($status)\");\necho $response;"},{"lang":"python","label":"Python","source":"import os\n\nimport requests\n\nresponse = requests.delete(\n    \"https://api.revkeen.com/v2/product-collections/11111111-1111-4111-8111-111111111111\",\n    headers={\"x-api-key\": os.environ[\"REVKEEN_API_KEY\"]},\n)\n\nresponse.raise_for_status()\nprint(response.json())"},{"lang":"javascript","label":"JavaScript","source":"const credential = process.env.REVKEEN_API_KEY;\nif (!credential) throw new Error(\"Set REVKEEN_API_KEY\");\nconst response = await fetch(\"https://api.revkeen.com/v2/product-collections/11111111-1111-4111-8111-111111111111\", {\n  method: \"DELETE\",\n  headers: {\n    \"x-api-key\": credential,\n  },\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"java","label":"Java","source":"import java.net.URI;\nimport java.net.http.HttpClient;\nimport java.net.http.HttpRequest;\nimport java.net.http.HttpResponse;\n\nHttpClient client = HttpClient.newHttpClient();\n\nHttpRequest request = HttpRequest.newBuilder()\n    .uri(URI.create(\"https://api.revkeen.com/v2/product-collections/11111111-1111-4111-8111-111111111111\"))\n    .header(\"x-api-key\", System.getenv(\"REVKEEN_API_KEY\"))\n    .method(\"DELETE\", HttpRequest.BodyPublishers.noBody())\n    .build();\n\nHttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());\nSystem.out.println(response.body());"},{"lang":"ruby","label":"Ruby","source":"require \"net/http\"\nrequire \"json\"\nrequire \"uri\"\n\nuri = URI(\"https://api.revkeen.com/v2/product-collections/11111111-1111-4111-8111-111111111111\")\nhttp = Net::HTTP.new(uri.host, uri.port)\nhttp.use_ssl = true\n\nrequest = Net::HTTP::Delete.new(uri)\nrequest[\"x-api-key\"] = ENV[\"REVKEEN_API_KEY\"]\n\nresponse = http.request(request)\nputs response.body"}],"security":[{"apiKey":["products:write"]},{"oauth2":["products:write"]}]}},"/checkout-sessions":{"post":{"operationId":"checkout_sessions_create","tags":["Checkout Sessions"],"x-fern-sdk-group-name":["checkout","sessions"],"summary":"Create a checkout session","description":"Create a checkout session for an invoice.\n\n---\n\n**Related endpoints**\n\n- `GET /checkout-sessions/{id}` — Retrieve a checkout session\n- `POST /checkout-sessions/{id}/expire` — Expire a checkout session\n\n**Common errors**\n\n- `400 invalid_request` — malformed payload or failed validation.\n- `401 authentication_error` — missing, invalid, expired, or revoked credential. Codes: `authentication_failed`, `invalid_api_key`, `expired_api_key`, `api_key_revoked`, `session_invalid`, `merchant_required`. Carries a `WWW-Authenticate: Bearer` challenge.\n- `404 resource_missing` — the referenced resource does not exist or is not visible to your key.\n\n**Idempotency**\n\nPass an `Idempotency-Key` header (UUID v4 recommended) to make retries safe. Keys are valid for 24 hours; see [the idempotency guide](/docs/fundamentals/idempotency).","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateCheckoutSessionInput"}}}},"responses":{"201":{"description":"Checkout session created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CheckoutSessionCreateResponse"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CheckoutSessionErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}},"headers":{"WWW-Authenticate":{"description":"RFC 6750 Bearer authentication challenge","schema":{"type":"string"},"example":"Bearer realm=\"revkeen-api\""}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"429":{"description":"Too Many Requests - Rate limit exceeded","headers":{"Retry-After":{"description":"Whole seconds until the caller may retry","schema":{"type":"string"},"example":"60"},"X-RateLimit-Limit":{"description":"Request quota for the current window","schema":{"type":"string"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window","schema":{"type":"string"}},"X-RateLimit-Reset":{"description":"UNIX timestamp in seconds when the window resets","schema":{"type":"string"}}}},"503":{"description":"No payment method available — the merchant has no online payment method enabled and live for this checkout, or none valid in the requested currency. `code` is `NO_PAYMENT_METHODS_AVAILABLE`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NoPaymentMethodsAvailableError"}}}}},"x-cli-group":"checkout-sessions","x-cli-name":"create","x-codeSamples":[{"lang":"curl","label":"cURL","source":"curl -X POST \"https://api.revkeen.com/v2/checkout-sessions\" \\\n  -H \"x-api-key: $REVKEEN_API_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"invoice_id\": \"11111111-1111-4111-8111-111111111111\"\n  }'"},{"lang":"typescript","label":"TypeScript (HTTP)","source":"const credential = process.env.REVKEEN_API_KEY;\nif (!credential) throw new Error(\"Set REVKEEN_API_KEY\");\nconst response = await fetch(\"https://api.revkeen.com/v2/checkout-sessions\", {\n  method: \"POST\",\n  headers: {\n    \"x-api-key\": credential,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n    \"invoice_id\": \"11111111-1111-4111-8111-111111111111\"\n  }),\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"go","label":"Go (HTTP)","source":"package main\nimport (\"fmt\"; \"io\"; \"net/http\"; \"os\"; \"strings\"; \"time\")\nfunc main() {\n  req, err := http.NewRequest(\"POST\", \"https://api.revkeen.com/v2/checkout-sessions\", strings.NewReader(\"{\\\"invoice_id\\\":\\\"11111111-1111-4111-8111-111111111111\\\"}\"))\n  if err != nil { panic(err) }\n  req.Header.Set(\"x-api-key\", os.Getenv(\"REVKEEN_API_KEY\"))\n  req.Header.Set(\"Content-Type\", \"application/json\")\n  client := &http.Client{Timeout: 10 * time.Second}\n  response, err := client.Do(req)\n  if err != nil { panic(err) }\n  defer response.Body.Close()\n  if response.StatusCode >= 400 { panic(fmt.Sprintf(\"RevKeen request failed (%d)\", response.StatusCode)) }\n  data, err := io.ReadAll(response.Body)\n  if err != nil { panic(err) }\n  fmt.Println(string(data))\n}"},{"lang":"php","label":"PHP (HTTP)","source":"<?php\n$request = curl_init(\"https://api.revkeen.com/v2/checkout-sessions\");\ncurl_setopt_array($request, [\n  CURLOPT_CUSTOMREQUEST => \"POST\",\n  CURLOPT_RETURNTRANSFER => true,\n  CURLOPT_TIMEOUT => 10,\n  CURLOPT_HTTPHEADER => [\"x-api-key: \" . getenv(\"REVKEEN_API_KEY\"), \"Content-Type: application/json\"],\n  CURLOPT_POSTFIELDS => '{\\\"invoice_id\\\":\\\"11111111-1111-4111-8111-111111111111\\\"}',\n]);\n$response = curl_exec($request);\nif ($response === false) throw new RuntimeException(curl_error($request));\n$status = curl_getinfo($request, CURLINFO_HTTP_CODE);\ncurl_close($request);\nif ($status >= 400) throw new RuntimeException(\"RevKeen request failed ($status)\");\necho $response;"},{"lang":"python","label":"Python","source":"import os\n\nimport requests\n\nresponse = requests.post(\n    \"https://api.revkeen.com/v2/checkout-sessions\",\n    headers={\"x-api-key\": os.environ[\"REVKEEN_API_KEY\"]},\n    json={\n        \"invoice_id\": \"11111111-1111-4111-8111-111111111111\",\n    },\n)\n\nresponse.raise_for_status()\nprint(response.json())"},{"lang":"javascript","label":"JavaScript","source":"const credential = process.env.REVKEEN_API_KEY;\nif (!credential) throw new Error(\"Set REVKEEN_API_KEY\");\nconst response = await fetch(\"https://api.revkeen.com/v2/checkout-sessions\", {\n  method: \"POST\",\n  headers: {\n    \"x-api-key\": credential,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n    \"invoice_id\": \"11111111-1111-4111-8111-111111111111\"\n  }),\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"java","label":"Java","source":"import java.net.URI;\nimport java.net.http.HttpClient;\nimport java.net.http.HttpRequest;\nimport java.net.http.HttpResponse;\n\nHttpClient client = HttpClient.newHttpClient();\n\nHttpRequest request = HttpRequest.newBuilder()\n    .uri(URI.create(\"https://api.revkeen.com/v2/checkout-sessions\"))\n    .header(\"x-api-key\", System.getenv(\"REVKEEN_API_KEY\"))\n    .header(\"Content-Type\", \"application/json\")\n    .method(\"POST\", HttpRequest.BodyPublishers.ofString(\"\"\"\n        {\n          \"invoice_id\": \"11111111-1111-4111-8111-111111111111\"\n        }\n        \"\"\"))\n    .build();\n\nHttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());\nSystem.out.println(response.body());"},{"lang":"ruby","label":"Ruby","source":"require \"net/http\"\nrequire \"json\"\nrequire \"uri\"\n\nuri = URI(\"https://api.revkeen.com/v2/checkout-sessions\")\nhttp = Net::HTTP.new(uri.host, uri.port)\nhttp.use_ssl = true\n\nrequest = Net::HTTP::Post.new(uri)\nrequest[\"x-api-key\"] = ENV[\"REVKEEN_API_KEY\"]\nrequest[\"Content-Type\"] = \"application/json\"\nrequest.body = {\n  \"invoice_id\" => \"11111111-1111-4111-8111-111111111111\",\n}.to_json\n\nresponse = http.request(request)\nputs response.body"}],"security":[{"apiKey":["checkout:write"]},{"oauth2":["checkout:write"]}]}},"/checkout-sessions/{id}":{"get":{"operationId":"checkout_sessions_get","tags":["Checkout Sessions"],"x-fern-sdk-group-name":["checkout","sessions"],"summary":"Retrieve a checkout session","description":"Retrieve a checkout session by its ID.\n\n---\n\n**Related endpoints**\n\n- `POST /checkout-sessions` — Create a checkout session\n- `POST /checkout-sessions/{id}/expire` — Expire a checkout session\n\n**Common errors**\n\n- `401 authentication_error` — missing, invalid, expired, or revoked credential. Codes: `authentication_failed`, `invalid_api_key`, `expired_api_key`, `api_key_revoked`, `session_invalid`, `merchant_required`. Carries a `WWW-Authenticate: Bearer` challenge.\n- `404 resource_missing` — the referenced resource does not exist or is not visible to your key.","parameters":[{"schema":{"type":"string","format":"uuid","description":"Checkout session ID.","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Checkout session ID.","name":"id","in":"path"}],"responses":{"200":{"description":"Checkout session retrieved","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CheckoutSessionRetrieveResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}},"headers":{"WWW-Authenticate":{"description":"RFC 6750 Bearer authentication challenge","schema":{"type":"string"},"example":"Bearer realm=\"revkeen-api\""}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"429":{"description":"Too Many Requests - Rate limit exceeded","headers":{"Retry-After":{"description":"Whole seconds until the caller may retry","schema":{"type":"string"},"example":"60"},"X-RateLimit-Limit":{"description":"Request quota for the current window","schema":{"type":"string"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window","schema":{"type":"string"}},"X-RateLimit-Reset":{"description":"UNIX timestamp in seconds when the window resets","schema":{"type":"string"}}}},"500":{"description":"Internal Server Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}}},"x-cli-group":"checkout-sessions","x-cli-name":"get","x-codeSamples":[{"lang":"curl","label":"cURL","source":"curl -X GET \"https://api.revkeen.com/v2/checkout-sessions/11111111-1111-4111-8111-111111111111\" \\\n  -H \"x-api-key: $REVKEEN_API_KEY\""},{"lang":"typescript","label":"TypeScript (SDK)","source":"// npm install @revkeen/sdk@1.20260822.1541\nimport { RevKeenClient } from \"@revkeen/sdk\";\nconst apiKey = process.env.REVKEEN_API_KEY;\nif (!apiKey) throw new Error(\"Set REVKEEN_API_KEY\");\nconst client = new RevKeenClient({ apiKey, baseUrl: \"https://api.revkeen.com/v2\" });\nconst result = await client.checkoutSessions.get(\"11111111-1111-4111-8111-111111111111\");\nconsole.log(result);"},{"lang":"go","label":"Go (HTTP)","source":"package main\nimport (\"fmt\"; \"io\"; \"net/http\"; \"os\"; \"strings\"; \"time\")\nfunc main() {\n  req, err := http.NewRequest(\"GET\", \"https://api.revkeen.com/v2/checkout-sessions/11111111-1111-4111-8111-111111111111\", strings.NewReader(\"\"))\n  if err != nil { panic(err) }\n  req.Header.Set(\"x-api-key\", os.Getenv(\"REVKEEN_API_KEY\"))\n  req.Header.Set(\"Content-Type\", \"application/json\")\n  client := &http.Client{Timeout: 10 * time.Second}\n  response, err := client.Do(req)\n  if err != nil { panic(err) }\n  defer response.Body.Close()\n  if response.StatusCode >= 400 { panic(fmt.Sprintf(\"RevKeen request failed (%d)\", response.StatusCode)) }\n  data, err := io.ReadAll(response.Body)\n  if err != nil { panic(err) }\n  fmt.Println(string(data))\n}"},{"lang":"php","label":"PHP (HTTP)","source":"<?php\n$request = curl_init(\"https://api.revkeen.com/v2/checkout-sessions/11111111-1111-4111-8111-111111111111\");\ncurl_setopt_array($request, [\n  CURLOPT_CUSTOMREQUEST => \"GET\",\n  CURLOPT_RETURNTRANSFER => true,\n  CURLOPT_TIMEOUT => 10,\n  CURLOPT_HTTPHEADER => [\"x-api-key: \" . getenv(\"REVKEEN_API_KEY\"), \"Content-Type: application/json\"],\n]);\n$response = curl_exec($request);\nif ($response === false) throw new RuntimeException(curl_error($request));\n$status = curl_getinfo($request, CURLINFO_HTTP_CODE);\ncurl_close($request);\nif ($status >= 400) throw new RuntimeException(\"RevKeen request failed ($status)\");\necho $response;"},{"lang":"python","label":"Python","source":"import os\n\nimport requests\n\nresponse = requests.get(\n    \"https://api.revkeen.com/v2/checkout-sessions/11111111-1111-4111-8111-111111111111\",\n    headers={\"x-api-key\": os.environ[\"REVKEEN_API_KEY\"]},\n)\n\nresponse.raise_for_status()\nprint(response.json())"},{"lang":"javascript","label":"JavaScript","source":"const credential = process.env.REVKEEN_API_KEY;\nif (!credential) throw new Error(\"Set REVKEEN_API_KEY\");\nconst response = await fetch(\"https://api.revkeen.com/v2/checkout-sessions/11111111-1111-4111-8111-111111111111\", {\n  method: \"GET\",\n  headers: {\n    \"x-api-key\": credential,\n  },\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"java","label":"Java","source":"import java.net.URI;\nimport java.net.http.HttpClient;\nimport java.net.http.HttpRequest;\nimport java.net.http.HttpResponse;\n\nHttpClient client = HttpClient.newHttpClient();\n\nHttpRequest request = HttpRequest.newBuilder()\n    .uri(URI.create(\"https://api.revkeen.com/v2/checkout-sessions/11111111-1111-4111-8111-111111111111\"))\n    .header(\"x-api-key\", System.getenv(\"REVKEEN_API_KEY\"))\n    .GET()\n    .build();\n\nHttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());\nSystem.out.println(response.body());"},{"lang":"ruby","label":"Ruby","source":"require \"net/http\"\nrequire \"json\"\nrequire \"uri\"\n\nuri = URI(\"https://api.revkeen.com/v2/checkout-sessions/11111111-1111-4111-8111-111111111111\")\nhttp = Net::HTTP.new(uri.host, uri.port)\nhttp.use_ssl = true\n\nrequest = Net::HTTP::Get.new(uri)\nrequest[\"x-api-key\"] = ENV[\"REVKEEN_API_KEY\"]\n\nresponse = http.request(request)\nputs response.body"}],"security":[{"apiKey":["checkout:read"]},{"oauth2":["checkout:read"]}]}},"/checkout-sessions/{id}/expire":{"post":{"operationId":"checkout_sessions_expire","tags":["Checkout Sessions"],"x-fern-sdk-group-name":["checkout","sessions"],"summary":"Expire a checkout session","description":"Manually expire an open checkout session. Only sessions with status 'open' or 'pending' and no active payment attempt can be expired.\n\n---\n\n**Related endpoints**\n\n- `POST /checkout-sessions` — Create a checkout session\n- `GET /checkout-sessions/{id}` — Retrieve a checkout session\n\n**Common errors**\n\n- `401 authentication_error` — missing, invalid, expired, or revoked credential. Codes: `authentication_failed`, `invalid_api_key`, `expired_api_key`, `api_key_revoked`, `session_invalid`, `merchant_required`. Carries a `WWW-Authenticate: Bearer` challenge.\n- `404 resource_missing` — the referenced resource does not exist or is not visible to your key.\n- `409 conflict` — Idempotency-Key collision with a different body, or a concurrent state-transition conflict.\n\n**Idempotency**\n\nPass an `Idempotency-Key` header (UUID v4 recommended) to make retries safe. Keys are valid for 24 hours; see [the idempotency guide](/docs/fundamentals/idempotency).","parameters":[{"schema":{"type":"string","format":"uuid","description":"Checkout session ID to expire.","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Checkout session ID to expire.","name":"id","in":"path"}],"responses":{"200":{"description":"Session expired","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CheckoutSessionExpireResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}},"headers":{"WWW-Authenticate":{"description":"RFC 6750 Bearer authentication challenge","schema":{"type":"string"},"example":"Bearer realm=\"revkeen-api\""}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotFoundError"}}}},"409":{"description":"Conflict — session is not in an expirable state","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"429":{"description":"Too Many Requests - Rate limit exceeded","headers":{"Retry-After":{"description":"Whole seconds until the caller may retry","schema":{"type":"string"},"example":"60"},"X-RateLimit-Limit":{"description":"Request quota for the current window","schema":{"type":"string"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window","schema":{"type":"string"}},"X-RateLimit-Reset":{"description":"UNIX timestamp in seconds when the window resets","schema":{"type":"string"}}}}},"x-cli-group":"checkout-sessions","x-cli-name":"expire","x-codeSamples":[{"lang":"curl","label":"cURL","source":"curl -X POST \"https://api.revkeen.com/v2/checkout-sessions/11111111-1111-4111-8111-111111111111/expire\" \\\n  -H \"x-api-key: $REVKEEN_API_KEY\""},{"lang":"typescript","label":"TypeScript (SDK)","source":"// npm install @revkeen/sdk@1.20260822.1541\nimport { RevKeenClient } from \"@revkeen/sdk\";\nconst apiKey = process.env.REVKEEN_API_KEY;\nif (!apiKey) throw new Error(\"Set REVKEEN_API_KEY\");\nconst client = new RevKeenClient({ apiKey, baseUrl: \"https://api.revkeen.com/v2\" });\nconst result = await client.checkoutSessions.expire(\"11111111-1111-4111-8111-111111111111\");\nconsole.log(result);"},{"lang":"go","label":"Go (HTTP)","source":"package main\nimport (\"fmt\"; \"io\"; \"net/http\"; \"os\"; \"strings\"; \"time\")\nfunc main() {\n  req, err := http.NewRequest(\"POST\", \"https://api.revkeen.com/v2/checkout-sessions/11111111-1111-4111-8111-111111111111/expire\", strings.NewReader(\"\"))\n  if err != nil { panic(err) }\n  req.Header.Set(\"x-api-key\", os.Getenv(\"REVKEEN_API_KEY\"))\n  req.Header.Set(\"Content-Type\", \"application/json\")\n  client := &http.Client{Timeout: 10 * time.Second}\n  response, err := client.Do(req)\n  if err != nil { panic(err) }\n  defer response.Body.Close()\n  if response.StatusCode >= 400 { panic(fmt.Sprintf(\"RevKeen request failed (%d)\", response.StatusCode)) }\n  data, err := io.ReadAll(response.Body)\n  if err != nil { panic(err) }\n  fmt.Println(string(data))\n}"},{"lang":"php","label":"PHP (HTTP)","source":"<?php\n$request = curl_init(\"https://api.revkeen.com/v2/checkout-sessions/11111111-1111-4111-8111-111111111111/expire\");\ncurl_setopt_array($request, [\n  CURLOPT_CUSTOMREQUEST => \"POST\",\n  CURLOPT_RETURNTRANSFER => true,\n  CURLOPT_TIMEOUT => 10,\n  CURLOPT_HTTPHEADER => [\"x-api-key: \" . getenv(\"REVKEEN_API_KEY\"), \"Content-Type: application/json\"],\n]);\n$response = curl_exec($request);\nif ($response === false) throw new RuntimeException(curl_error($request));\n$status = curl_getinfo($request, CURLINFO_HTTP_CODE);\ncurl_close($request);\nif ($status >= 400) throw new RuntimeException(\"RevKeen request failed ($status)\");\necho $response;"},{"lang":"python","label":"Python","source":"import os\n\nimport requests\n\nresponse = requests.post(\n    \"https://api.revkeen.com/v2/checkout-sessions/11111111-1111-4111-8111-111111111111/expire\",\n    headers={\"x-api-key\": os.environ[\"REVKEEN_API_KEY\"]},\n)\n\nresponse.raise_for_status()\nprint(response.json())"},{"lang":"javascript","label":"JavaScript","source":"const credential = process.env.REVKEEN_API_KEY;\nif (!credential) throw new Error(\"Set REVKEEN_API_KEY\");\nconst response = await fetch(\"https://api.revkeen.com/v2/checkout-sessions/11111111-1111-4111-8111-111111111111/expire\", {\n  method: \"POST\",\n  headers: {\n    \"x-api-key\": credential,\n  },\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"java","label":"Java","source":"import java.net.URI;\nimport java.net.http.HttpClient;\nimport java.net.http.HttpRequest;\nimport java.net.http.HttpResponse;\n\nHttpClient client = HttpClient.newHttpClient();\n\nHttpRequest request = HttpRequest.newBuilder()\n    .uri(URI.create(\"https://api.revkeen.com/v2/checkout-sessions/11111111-1111-4111-8111-111111111111/expire\"))\n    .header(\"x-api-key\", System.getenv(\"REVKEEN_API_KEY\"))\n    .method(\"POST\", HttpRequest.BodyPublishers.noBody())\n    .build();\n\nHttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());\nSystem.out.println(response.body());"},{"lang":"ruby","label":"Ruby","source":"require \"net/http\"\nrequire \"json\"\nrequire \"uri\"\n\nuri = URI(\"https://api.revkeen.com/v2/checkout-sessions/11111111-1111-4111-8111-111111111111/expire\")\nhttp = Net::HTTP.new(uri.host, uri.port)\nhttp.use_ssl = true\n\nrequest = Net::HTTP::Post.new(uri)\nrequest[\"x-api-key\"] = ENV[\"REVKEEN_API_KEY\"]\n\nresponse = http.request(request)\nputs response.body"}],"security":[{"apiKey":["checkout:write"]},{"oauth2":["checkout:write"]}]}},"/cart-sessions":{"post":{"operationId":"cart_sessions_create","tags":["Cart Sessions"],"x-fern-sdk-group-name":["checkout","cart-sessions"],"summary":"Create a cart session","description":"Create an empty (or pre-populated) cart session. The returned cart is `open` and mutable until it is converted into a checkout session, or until it is swept to `abandoned` / `expired`.\n\n---\n\n**Related endpoints**\n\n- `GET /cart-sessions/{id}` — Retrieve a cart session\n- `POST /cart-sessions/{id}/convert` — Convert to a checkout session\n\n**Idempotency**\n\nPass an `Idempotency-Key` header (UUID v4 recommended) to make retries safe. Keys are valid for 24 hours; see [the idempotency guide](/docs/fundamentals/idempotency).\n\n---\n\n**Related endpoints**\n\n- `GET /cart-sessions/{id}` — Retrieve a cart session\n- `POST /cart-sessions/{id}/line-items` — Add a line item to a cart session\n- `PATCH /cart-sessions/{id}/line-items/{line_id}` — Update a line item's quantity\n- `DELETE /cart-sessions/{id}/line-items/{line_id}` — Remove a line item from a cart session\n- `POST /cart-sessions/{id}/add-ons` — Toggle an add-on on a cart session\n- `POST /cart-sessions/{id}/discount-code` — Set or clear a cart discount code\n- `POST /cart-sessions/{id}/contact` — Capture customer email + consent on a cart\n- `POST /cart-sessions/{id}/convert` — Convert a cart session into a checkout session\n\n**Common errors**\n\n- `400 invalid_request` — malformed payload or failed validation.\n- `401 authentication_error` — missing, invalid, expired, or revoked credential. Codes: `authentication_failed`, `invalid_api_key`, `expired_api_key`, `api_key_revoked`, `session_invalid`, `merchant_required`. Carries a `WWW-Authenticate: Bearer` challenge.\n- `404 resource_missing` — the referenced resource does not exist or is not visible to your key.\n- `422 unprocessable_entity` — business-rule failure (for example, refunding more than the original charge).\n\n**Idempotency**\n\nPass an `Idempotency-Key` header (UUID v4 recommended) to make retries safe. Keys are valid for 24 hours; see [the idempotency guide](/docs/fundamentals/idempotency).","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateCartSessionInput"}}}},"responses":{"201":{"description":"Cart session created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CartSessionResponse"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CartSessionErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}},"headers":{"WWW-Authenticate":{"description":"RFC 6750 Bearer authentication challenge","schema":{"type":"string"},"example":"Bearer realm=\"revkeen-api\""}}},"404":{"description":"Not Found — cart or line item not found, or not visible to your key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CartSessionErrorResponse"}}}},"422":{"description":"Unprocessable — seed payment link is not cart-enabled (CART_NOT_ENABLED_FOR_LINK).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CartSessionErrorResponse"}}}},"429":{"description":"Too Many Requests - Rate limit exceeded","headers":{"Retry-After":{"description":"Whole seconds until the caller may retry","schema":{"type":"string"},"example":"60"},"X-RateLimit-Limit":{"description":"Request quota for the current window","schema":{"type":"string"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window","schema":{"type":"string"}},"X-RateLimit-Reset":{"description":"UNIX timestamp in seconds when the window resets","schema":{"type":"string"}}}}},"x-cli-group":"cart-sessions","x-cli-name":"create","x-codeSamples":[{"lang":"curl","label":"cURL","source":"curl -X POST \"https://api.revkeen.com/v2/cart-sessions\" \\\n  -H \"x-api-key: $REVKEEN_API_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"currency\": \"GBP\"\n  }'"},{"lang":"typescript","label":"TypeScript (SDK)","source":"// npm install @revkeen/sdk@1.20260822.1541\nimport { RevKeenClient } from \"@revkeen/sdk\";\nconst apiKey = process.env.REVKEEN_API_KEY;\nif (!apiKey) throw new Error(\"Set REVKEEN_API_KEY\");\nconst client = new RevKeenClient({ apiKey, baseUrl: \"https://api.revkeen.com/v2\" });\nconst result = await client.cart.sessionsCreate({\n  \"currency\": \"GBP\"\n});\nconsole.log(result);"},{"lang":"go","label":"Go (HTTP)","source":"package main\nimport (\"fmt\"; \"io\"; \"net/http\"; \"os\"; \"strings\"; \"time\")\nfunc main() {\n  req, err := http.NewRequest(\"POST\", \"https://api.revkeen.com/v2/cart-sessions\", strings.NewReader(\"{\\\"currency\\\":\\\"GBP\\\"}\"))\n  if err != nil { panic(err) }\n  req.Header.Set(\"x-api-key\", os.Getenv(\"REVKEEN_API_KEY\"))\n  req.Header.Set(\"Content-Type\", \"application/json\")\n  client := &http.Client{Timeout: 10 * time.Second}\n  response, err := client.Do(req)\n  if err != nil { panic(err) }\n  defer response.Body.Close()\n  if response.StatusCode >= 400 { panic(fmt.Sprintf(\"RevKeen request failed (%d)\", response.StatusCode)) }\n  data, err := io.ReadAll(response.Body)\n  if err != nil { panic(err) }\n  fmt.Println(string(data))\n}"},{"lang":"php","label":"PHP (HTTP)","source":"<?php\n$request = curl_init(\"https://api.revkeen.com/v2/cart-sessions\");\ncurl_setopt_array($request, [\n  CURLOPT_CUSTOMREQUEST => \"POST\",\n  CURLOPT_RETURNTRANSFER => true,\n  CURLOPT_TIMEOUT => 10,\n  CURLOPT_HTTPHEADER => [\"x-api-key: \" . getenv(\"REVKEEN_API_KEY\"), \"Content-Type: application/json\"],\n  CURLOPT_POSTFIELDS => '{\\\"currency\\\":\\\"GBP\\\"}',\n]);\n$response = curl_exec($request);\nif ($response === false) throw new RuntimeException(curl_error($request));\n$status = curl_getinfo($request, CURLINFO_HTTP_CODE);\ncurl_close($request);\nif ($status >= 400) throw new RuntimeException(\"RevKeen request failed ($status)\");\necho $response;"},{"lang":"python","label":"Python","source":"import os\n\nimport requests\n\nresponse = requests.post(\n    \"https://api.revkeen.com/v2/cart-sessions\",\n    headers={\"x-api-key\": os.environ[\"REVKEEN_API_KEY\"]},\n    json={\n        \"currency\": \"GBP\",\n    },\n)\n\nresponse.raise_for_status()\nprint(response.json())"},{"lang":"javascript","label":"JavaScript","source":"const credential = process.env.REVKEEN_API_KEY;\nif (!credential) throw new Error(\"Set REVKEEN_API_KEY\");\nconst response = await fetch(\"https://api.revkeen.com/v2/cart-sessions\", {\n  method: \"POST\",\n  headers: {\n    \"x-api-key\": credential,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n    \"currency\": \"GBP\"\n  }),\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"java","label":"Java","source":"import java.net.URI;\nimport java.net.http.HttpClient;\nimport java.net.http.HttpRequest;\nimport java.net.http.HttpResponse;\n\nHttpClient client = HttpClient.newHttpClient();\n\nHttpRequest request = HttpRequest.newBuilder()\n    .uri(URI.create(\"https://api.revkeen.com/v2/cart-sessions\"))\n    .header(\"x-api-key\", System.getenv(\"REVKEEN_API_KEY\"))\n    .header(\"Content-Type\", \"application/json\")\n    .method(\"POST\", HttpRequest.BodyPublishers.ofString(\"\"\"\n        {\n          \"currency\": \"GBP\"\n        }\n        \"\"\"))\n    .build();\n\nHttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());\nSystem.out.println(response.body());"},{"lang":"ruby","label":"Ruby","source":"require \"net/http\"\nrequire \"json\"\nrequire \"uri\"\n\nuri = URI(\"https://api.revkeen.com/v2/cart-sessions\")\nhttp = Net::HTTP.new(uri.host, uri.port)\nhttp.use_ssl = true\n\nrequest = Net::HTTP::Post.new(uri)\nrequest[\"x-api-key\"] = ENV[\"REVKEEN_API_KEY\"]\nrequest[\"Content-Type\"] = \"application/json\"\nrequest.body = {\n  \"currency\" => \"GBP\",\n}.to_json\n\nresponse = http.request(request)\nputs response.body"}],"security":[{"apiKey":["cart:write"]},{"oauth2":["cart:write"]}]}},"/cart-sessions/{id}":{"get":{"operationId":"cart_sessions_get","tags":["Cart Sessions"],"x-fern-sdk-group-name":["checkout","cart-sessions"],"summary":"Retrieve a cart session","description":"Fetch the current state of a cart session by id. Returns the same shape as the create response.\n\n---\n\n**Related endpoints**\n\n- `POST /cart-sessions` — Create a cart session\n- `POST /cart-sessions/{id}/line-items` — Add a line item to a cart session\n- `PATCH /cart-sessions/{id}/line-items/{line_id}` — Update a line item's quantity\n- `DELETE /cart-sessions/{id}/line-items/{line_id}` — Remove a line item from a cart session\n- `POST /cart-sessions/{id}/add-ons` — Toggle an add-on on a cart session\n- `POST /cart-sessions/{id}/discount-code` — Set or clear a cart discount code\n- `POST /cart-sessions/{id}/contact` — Capture customer email + consent on a cart\n- `POST /cart-sessions/{id}/convert` — Convert a cart session into a checkout session\n\n**Common errors**\n\n- `401 authentication_error` — missing, invalid, expired, or revoked credential. Codes: `authentication_failed`, `invalid_api_key`, `expired_api_key`, `api_key_revoked`, `session_invalid`, `merchant_required`. Carries a `WWW-Authenticate: Bearer` challenge.\n- `404 resource_missing` — the referenced resource does not exist or is not visible to your key.","parameters":[{"schema":{"type":"string","format":"uuid","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"Cart session","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CartSessionResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}},"headers":{"WWW-Authenticate":{"description":"RFC 6750 Bearer authentication challenge","schema":{"type":"string"},"example":"Bearer realm=\"revkeen-api\""}}},"404":{"description":"Not Found — cart or line item not found, or not visible to your key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CartSessionErrorResponse"}}}},"429":{"description":"Too Many Requests - Rate limit exceeded","headers":{"Retry-After":{"description":"Whole seconds until the caller may retry","schema":{"type":"string"},"example":"60"},"X-RateLimit-Limit":{"description":"Request quota for the current window","schema":{"type":"string"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window","schema":{"type":"string"}},"X-RateLimit-Reset":{"description":"UNIX timestamp in seconds when the window resets","schema":{"type":"string"}}}}},"x-cli-group":"cart-sessions","x-cli-name":"get","x-codeSamples":[{"lang":"curl","label":"cURL","source":"curl -X GET \"https://api.revkeen.com/v2/cart-sessions/11111111-1111-4111-8111-111111111111\" \\\n  -H \"x-api-key: $REVKEEN_API_KEY\""},{"lang":"typescript","label":"TypeScript (SDK)","source":"// npm install @revkeen/sdk@1.20260822.1541\nimport { RevKeenClient } from \"@revkeen/sdk\";\nconst apiKey = process.env.REVKEEN_API_KEY;\nif (!apiKey) throw new Error(\"Set REVKEEN_API_KEY\");\nconst client = new RevKeenClient({ apiKey, baseUrl: \"https://api.revkeen.com/v2\" });\nconst result = await client.cart.sessionsGet(\"11111111-1111-4111-8111-111111111111\");\nconsole.log(result);"},{"lang":"go","label":"Go (HTTP)","source":"package main\nimport (\"fmt\"; \"io\"; \"net/http\"; \"os\"; \"strings\"; \"time\")\nfunc main() {\n  req, err := http.NewRequest(\"GET\", \"https://api.revkeen.com/v2/cart-sessions/11111111-1111-4111-8111-111111111111\", strings.NewReader(\"\"))\n  if err != nil { panic(err) }\n  req.Header.Set(\"x-api-key\", os.Getenv(\"REVKEEN_API_KEY\"))\n  req.Header.Set(\"Content-Type\", \"application/json\")\n  client := &http.Client{Timeout: 10 * time.Second}\n  response, err := client.Do(req)\n  if err != nil { panic(err) }\n  defer response.Body.Close()\n  if response.StatusCode >= 400 { panic(fmt.Sprintf(\"RevKeen request failed (%d)\", response.StatusCode)) }\n  data, err := io.ReadAll(response.Body)\n  if err != nil { panic(err) }\n  fmt.Println(string(data))\n}"},{"lang":"php","label":"PHP (HTTP)","source":"<?php\n$request = curl_init(\"https://api.revkeen.com/v2/cart-sessions/11111111-1111-4111-8111-111111111111\");\ncurl_setopt_array($request, [\n  CURLOPT_CUSTOMREQUEST => \"GET\",\n  CURLOPT_RETURNTRANSFER => true,\n  CURLOPT_TIMEOUT => 10,\n  CURLOPT_HTTPHEADER => [\"x-api-key: \" . getenv(\"REVKEEN_API_KEY\"), \"Content-Type: application/json\"],\n]);\n$response = curl_exec($request);\nif ($response === false) throw new RuntimeException(curl_error($request));\n$status = curl_getinfo($request, CURLINFO_HTTP_CODE);\ncurl_close($request);\nif ($status >= 400) throw new RuntimeException(\"RevKeen request failed ($status)\");\necho $response;"},{"lang":"python","label":"Python","source":"import os\n\nimport requests\n\nresponse = requests.get(\n    \"https://api.revkeen.com/v2/cart-sessions/11111111-1111-4111-8111-111111111111\",\n    headers={\"x-api-key\": os.environ[\"REVKEEN_API_KEY\"]},\n)\n\nresponse.raise_for_status()\nprint(response.json())"},{"lang":"javascript","label":"JavaScript","source":"const credential = process.env.REVKEEN_API_KEY;\nif (!credential) throw new Error(\"Set REVKEEN_API_KEY\");\nconst response = await fetch(\"https://api.revkeen.com/v2/cart-sessions/11111111-1111-4111-8111-111111111111\", {\n  method: \"GET\",\n  headers: {\n    \"x-api-key\": credential,\n  },\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"java","label":"Java","source":"import java.net.URI;\nimport java.net.http.HttpClient;\nimport java.net.http.HttpRequest;\nimport java.net.http.HttpResponse;\n\nHttpClient client = HttpClient.newHttpClient();\n\nHttpRequest request = HttpRequest.newBuilder()\n    .uri(URI.create(\"https://api.revkeen.com/v2/cart-sessions/11111111-1111-4111-8111-111111111111\"))\n    .header(\"x-api-key\", System.getenv(\"REVKEEN_API_KEY\"))\n    .GET()\n    .build();\n\nHttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());\nSystem.out.println(response.body());"},{"lang":"ruby","label":"Ruby","source":"require \"net/http\"\nrequire \"json\"\nrequire \"uri\"\n\nuri = URI(\"https://api.revkeen.com/v2/cart-sessions/11111111-1111-4111-8111-111111111111\")\nhttp = Net::HTTP.new(uri.host, uri.port)\nhttp.use_ssl = true\n\nrequest = Net::HTTP::Get.new(uri)\nrequest[\"x-api-key\"] = ENV[\"REVKEEN_API_KEY\"]\n\nresponse = http.request(request)\nputs response.body"}],"security":[{"apiKey":["cart:read"]},{"oauth2":["cart:read"]}]}},"/cart-sessions/{id}/line-items":{"post":{"operationId":"cart_sessions_add_line_item","tags":["Cart Sessions"],"x-fern-sdk-group-name":["checkout","cart-sessions","line-items"],"summary":"Add a line item to a cart session","description":"Append a line item to an open cart session. Subtotals are recomputed atomically; the updated cart is returned.\n\n---\n\n**Related endpoints**\n\n- `POST /cart-sessions` — Create a cart session\n- `GET /cart-sessions/{id}` — Retrieve a cart session\n- `PATCH /cart-sessions/{id}/line-items/{line_id}` — Update a line item's quantity\n- `DELETE /cart-sessions/{id}/line-items/{line_id}` — Remove a line item from a cart session\n- `POST /cart-sessions/{id}/add-ons` — Toggle an add-on on a cart session\n- `POST /cart-sessions/{id}/discount-code` — Set or clear a cart discount code\n- `POST /cart-sessions/{id}/contact` — Capture customer email + consent on a cart\n- `POST /cart-sessions/{id}/convert` — Convert a cart session into a checkout session\n\n**Common errors**\n\n- `400 invalid_request` — malformed payload or failed validation.\n- `401 authentication_error` — missing, invalid, expired, or revoked credential. Codes: `authentication_failed`, `invalid_api_key`, `expired_api_key`, `api_key_revoked`, `session_invalid`, `merchant_required`. Carries a `WWW-Authenticate: Bearer` challenge.\n- `404 resource_missing` — the referenced resource does not exist or is not visible to your key.\n- `409 conflict` — Idempotency-Key collision with a different body, or a concurrent state-transition conflict.\n\n**Idempotency**\n\nPass an `Idempotency-Key` header (UUID v4 recommended) to make retries safe. Keys are valid for 24 hours; see [the idempotency guide](/docs/fundamentals/idempotency).","parameters":[{"schema":{"type":"string","format":"uuid","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"name":"id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CartLineItemInput"}}}},"responses":{"201":{"description":"Line item added; updated cart returned","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CartSessionResponse"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CartSessionErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}},"headers":{"WWW-Authenticate":{"description":"RFC 6750 Bearer authentication challenge","schema":{"type":"string"},"example":"Bearer realm=\"revkeen-api\""}}},"404":{"description":"Not Found — cart or line item not found, or not visible to your key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CartSessionErrorResponse"}}}},"409":{"description":"Conflict — cart is closed, empty (on convert), or has a pending discount code that blocks convert.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CartSessionErrorResponse"}}}},"429":{"description":"Too Many Requests - Rate limit exceeded","headers":{"Retry-After":{"description":"Whole seconds until the caller may retry","schema":{"type":"string"},"example":"60"},"X-RateLimit-Limit":{"description":"Request quota for the current window","schema":{"type":"string"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window","schema":{"type":"string"}},"X-RateLimit-Reset":{"description":"UNIX timestamp in seconds when the window resets","schema":{"type":"string"}}}}},"x-cli-group":"cart-sessions","x-cli-name":"add-line-item","x-codeSamples":[{"lang":"curl","label":"cURL","source":"curl -X POST \"https://api.revkeen.com/v2/cart-sessions/11111111-1111-4111-8111-111111111111/line-items\" \\\n  -H \"x-api-key: $REVKEEN_API_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"product_id\": \"11111111-1111-4111-8111-111111111111\",\n    \"name\": \"Annual plan\",\n    \"quantity\": 1,\n    \"unit_price_minor\": 9900,\n    \"currency\": \"GBP\"\n  }'"},{"lang":"typescript","label":"TypeScript (SDK)","source":"// npm install @revkeen/sdk@1.20260822.1541\nimport { RevKeenClient } from \"@revkeen/sdk\";\nconst apiKey = process.env.REVKEEN_API_KEY;\nif (!apiKey) throw new Error(\"Set REVKEEN_API_KEY\");\nconst client = new RevKeenClient({ apiKey, baseUrl: \"https://api.revkeen.com/v2\" });\nconst result = await client.cart.sessionsAddLineItem(\"11111111-1111-4111-8111-111111111111\", {\n  \"product_id\": \"11111111-1111-4111-8111-111111111111\",\n  \"name\": \"Annual plan\",\n  \"quantity\": 1,\n  \"unit_price_minor\": 9900,\n  \"currency\": \"GBP\"\n});\nconsole.log(result);"},{"lang":"go","label":"Go (HTTP)","source":"package main\nimport (\"fmt\"; \"io\"; \"net/http\"; \"os\"; \"strings\"; \"time\")\nfunc main() {\n  req, err := http.NewRequest(\"POST\", \"https://api.revkeen.com/v2/cart-sessions/11111111-1111-4111-8111-111111111111/line-items\", strings.NewReader(\"{\\\"product_id\\\":\\\"11111111-1111-4111-8111-111111111111\\\",\\\"name\\\":\\\"Annual plan\\\",\\\"quantity\\\":1,\\\"unit_price_minor\\\":9900,\\\"currency\\\":\\\"GBP\\\"}\"))\n  if err != nil { panic(err) }\n  req.Header.Set(\"x-api-key\", os.Getenv(\"REVKEEN_API_KEY\"))\n  req.Header.Set(\"Content-Type\", \"application/json\")\n  client := &http.Client{Timeout: 10 * time.Second}\n  response, err := client.Do(req)\n  if err != nil { panic(err) }\n  defer response.Body.Close()\n  if response.StatusCode >= 400 { panic(fmt.Sprintf(\"RevKeen request failed (%d)\", response.StatusCode)) }\n  data, err := io.ReadAll(response.Body)\n  if err != nil { panic(err) }\n  fmt.Println(string(data))\n}"},{"lang":"php","label":"PHP (HTTP)","source":"<?php\n$request = curl_init(\"https://api.revkeen.com/v2/cart-sessions/11111111-1111-4111-8111-111111111111/line-items\");\ncurl_setopt_array($request, [\n  CURLOPT_CUSTOMREQUEST => \"POST\",\n  CURLOPT_RETURNTRANSFER => true,\n  CURLOPT_TIMEOUT => 10,\n  CURLOPT_HTTPHEADER => [\"x-api-key: \" . getenv(\"REVKEEN_API_KEY\"), \"Content-Type: application/json\"],\n  CURLOPT_POSTFIELDS => '{\\\"product_id\\\":\\\"11111111-1111-4111-8111-111111111111\\\",\\\"name\\\":\\\"Annual plan\\\",\\\"quantity\\\":1,\\\"unit_price_minor\\\":9900,\\\"currency\\\":\\\"GBP\\\"}',\n]);\n$response = curl_exec($request);\nif ($response === false) throw new RuntimeException(curl_error($request));\n$status = curl_getinfo($request, CURLINFO_HTTP_CODE);\ncurl_close($request);\nif ($status >= 400) throw new RuntimeException(\"RevKeen request failed ($status)\");\necho $response;"},{"lang":"python","label":"Python","source":"import os\n\nimport requests\n\nresponse = requests.post(\n    \"https://api.revkeen.com/v2/cart-sessions/11111111-1111-4111-8111-111111111111/line-items\",\n    headers={\"x-api-key\": os.environ[\"REVKEEN_API_KEY\"]},\n    json={\n        \"product_id\": \"11111111-1111-4111-8111-111111111111\",\n        \"name\": \"Annual plan\",\n        \"quantity\": 1,\n        \"unit_price_minor\": 9900,\n        \"currency\": \"GBP\",\n    },\n)\n\nresponse.raise_for_status()\nprint(response.json())"},{"lang":"javascript","label":"JavaScript","source":"const credential = process.env.REVKEEN_API_KEY;\nif (!credential) throw new Error(\"Set REVKEEN_API_KEY\");\nconst response = await fetch(\"https://api.revkeen.com/v2/cart-sessions/11111111-1111-4111-8111-111111111111/line-items\", {\n  method: \"POST\",\n  headers: {\n    \"x-api-key\": credential,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n    \"product_id\": \"11111111-1111-4111-8111-111111111111\",\n    \"name\": \"Annual plan\",\n    \"quantity\": 1,\n    \"unit_price_minor\": 9900,\n    \"currency\": \"GBP\"\n  }),\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"java","label":"Java","source":"import java.net.URI;\nimport java.net.http.HttpClient;\nimport java.net.http.HttpRequest;\nimport java.net.http.HttpResponse;\n\nHttpClient client = HttpClient.newHttpClient();\n\nHttpRequest request = HttpRequest.newBuilder()\n    .uri(URI.create(\"https://api.revkeen.com/v2/cart-sessions/11111111-1111-4111-8111-111111111111/line-items\"))\n    .header(\"x-api-key\", System.getenv(\"REVKEEN_API_KEY\"))\n    .header(\"Content-Type\", \"application/json\")\n    .method(\"POST\", HttpRequest.BodyPublishers.ofString(\"\"\"\n        {\n          \"product_id\": \"11111111-1111-4111-8111-111111111111\",\n          \"name\": \"Annual plan\",\n          \"quantity\": 1,\n          \"unit_price_minor\": 9900,\n          \"currency\": \"GBP\"\n        }\n        \"\"\"))\n    .build();\n\nHttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());\nSystem.out.println(response.body());"},{"lang":"ruby","label":"Ruby","source":"require \"net/http\"\nrequire \"json\"\nrequire \"uri\"\n\nuri = URI(\"https://api.revkeen.com/v2/cart-sessions/11111111-1111-4111-8111-111111111111/line-items\")\nhttp = Net::HTTP.new(uri.host, uri.port)\nhttp.use_ssl = true\n\nrequest = Net::HTTP::Post.new(uri)\nrequest[\"x-api-key\"] = ENV[\"REVKEEN_API_KEY\"]\nrequest[\"Content-Type\"] = \"application/json\"\nrequest.body = {\n  \"product_id\" => \"11111111-1111-4111-8111-111111111111\",\n  \"name\" => \"Annual plan\",\n  \"quantity\" => 1,\n  \"unit_price_minor\" => 9900,\n  \"currency\" => \"GBP\",\n}.to_json\n\nresponse = http.request(request)\nputs response.body"}],"security":[{"apiKey":["cart:write"]},{"oauth2":["cart:write"]}]}},"/cart-sessions/{id}/line-items/{line_id}":{"patch":{"operationId":"cart_sessions_update_line_item","tags":["Cart Sessions"],"x-fern-sdk-group-name":["checkout","cart-sessions","line-items"],"summary":"Update a line item's quantity","description":"Change the quantity of a single line item on an open cart. Other fields on the line item are immutable; recreate the item to change them.\n\n---\n\n**Related endpoints**\n\n- `POST /cart-sessions` — Create a cart session\n- `GET /cart-sessions/{id}` — Retrieve a cart session\n- `POST /cart-sessions/{id}/line-items` — Add a line item to a cart session\n- `DELETE /cart-sessions/{id}/line-items/{line_id}` — Remove a line item from a cart session\n- `POST /cart-sessions/{id}/add-ons` — Toggle an add-on on a cart session\n- `POST /cart-sessions/{id}/discount-code` — Set or clear a cart discount code\n- `POST /cart-sessions/{id}/contact` — Capture customer email + consent on a cart\n- `POST /cart-sessions/{id}/convert` — Convert a cart session into a checkout session\n\n**Common errors**\n\n- `400 invalid_request` — malformed payload or failed validation.\n- `401 authentication_error` — missing, invalid, expired, or revoked credential. Codes: `authentication_failed`, `invalid_api_key`, `expired_api_key`, `api_key_revoked`, `session_invalid`, `merchant_required`. Carries a `WWW-Authenticate: Bearer` challenge.\n- `404 resource_missing` — the referenced resource does not exist or is not visible to your key.\n- `409 conflict` — Idempotency-Key collision with a different body, or a concurrent state-transition conflict.\n\n**Idempotency**\n\nPass an `Idempotency-Key` header (UUID v4 recommended) to make retries safe. Keys are valid for 24 hours; see [the idempotency guide](/docs/fundamentals/idempotency).","parameters":[{"schema":{"type":"string","format":"uuid","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"name":"id","in":"path"},{"schema":{"type":"string","description":"Cart line item id (the `id` field returned on `cart_session.line_items[]`).","example":"cli_550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Cart line item id (the `id` field returned on `cart_session.line_items[]`).","name":"line_id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateCartLineItemInput"}}}},"responses":{"200":{"description":"Cart session","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CartSessionResponse"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CartSessionErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}},"headers":{"WWW-Authenticate":{"description":"RFC 6750 Bearer authentication challenge","schema":{"type":"string"},"example":"Bearer realm=\"revkeen-api\""}}},"404":{"description":"Not Found — cart or line item not found, or not visible to your key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CartSessionErrorResponse"}}}},"409":{"description":"Conflict — cart is closed, empty (on convert), or has a pending discount code that blocks convert.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CartSessionErrorResponse"}}}},"429":{"description":"Too Many Requests - Rate limit exceeded","headers":{"Retry-After":{"description":"Whole seconds until the caller may retry","schema":{"type":"string"},"example":"60"},"X-RateLimit-Limit":{"description":"Request quota for the current window","schema":{"type":"string"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window","schema":{"type":"string"}},"X-RateLimit-Reset":{"description":"UNIX timestamp in seconds when the window resets","schema":{"type":"string"}}}}},"x-cli-group":"cart-sessions","x-cli-name":"update-line-item","x-codeSamples":[{"lang":"curl","label":"cURL","source":"curl -X PATCH \"https://api.revkeen.com/v2/cart-sessions/11111111-1111-4111-8111-111111111111/line-items/cli_550e8400-e29b-41d4-a716-446655440000\" \\\n  -H \"x-api-key: $REVKEEN_API_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"quantity\": 2\n  }'"},{"lang":"typescript","label":"TypeScript (SDK)","source":"// npm install @revkeen/sdk@1.20260822.1541\nimport { RevKeenClient } from \"@revkeen/sdk\";\nconst apiKey = process.env.REVKEEN_API_KEY;\nif (!apiKey) throw new Error(\"Set REVKEEN_API_KEY\");\nconst client = new RevKeenClient({ apiKey, baseUrl: \"https://api.revkeen.com/v2\" });\nconst result = await client.cart.sessionsUpdateLineItem({\n  \"path\": {\n    \"id\": \"11111111-1111-4111-8111-111111111111\",\n    \"lineId\": \"cli_550e8400-e29b-41d4-a716-446655440000\"\n  },\n  \"body\": {\n    \"quantity\": 2\n  }\n});\nconsole.log(result);"},{"lang":"go","label":"Go (HTTP)","source":"package main\nimport (\"fmt\"; \"io\"; \"net/http\"; \"os\"; \"strings\"; \"time\")\nfunc main() {\n  req, err := http.NewRequest(\"PATCH\", \"https://api.revkeen.com/v2/cart-sessions/11111111-1111-4111-8111-111111111111/line-items/cli_550e8400-e29b-41d4-a716-446655440000\", strings.NewReader(\"{\\\"quantity\\\":2}\"))\n  if err != nil { panic(err) }\n  req.Header.Set(\"x-api-key\", os.Getenv(\"REVKEEN_API_KEY\"))\n  req.Header.Set(\"Content-Type\", \"application/json\")\n  client := &http.Client{Timeout: 10 * time.Second}\n  response, err := client.Do(req)\n  if err != nil { panic(err) }\n  defer response.Body.Close()\n  if response.StatusCode >= 400 { panic(fmt.Sprintf(\"RevKeen request failed (%d)\", response.StatusCode)) }\n  data, err := io.ReadAll(response.Body)\n  if err != nil { panic(err) }\n  fmt.Println(string(data))\n}"},{"lang":"php","label":"PHP (HTTP)","source":"<?php\n$request = curl_init(\"https://api.revkeen.com/v2/cart-sessions/11111111-1111-4111-8111-111111111111/line-items/cli_550e8400-e29b-41d4-a716-446655440000\");\ncurl_setopt_array($request, [\n  CURLOPT_CUSTOMREQUEST => \"PATCH\",\n  CURLOPT_RETURNTRANSFER => true,\n  CURLOPT_TIMEOUT => 10,\n  CURLOPT_HTTPHEADER => [\"x-api-key: \" . getenv(\"REVKEEN_API_KEY\"), \"Content-Type: application/json\"],\n  CURLOPT_POSTFIELDS => '{\\\"quantity\\\":2}',\n]);\n$response = curl_exec($request);\nif ($response === false) throw new RuntimeException(curl_error($request));\n$status = curl_getinfo($request, CURLINFO_HTTP_CODE);\ncurl_close($request);\nif ($status >= 400) throw new RuntimeException(\"RevKeen request failed ($status)\");\necho $response;"},{"lang":"python","label":"Python","source":"import os\n\nimport requests\n\nresponse = requests.patch(\n    \"https://api.revkeen.com/v2/cart-sessions/11111111-1111-4111-8111-111111111111/line-items/cli_550e8400-e29b-41d4-a716-446655440000\",\n    headers={\"x-api-key\": os.environ[\"REVKEEN_API_KEY\"]},\n    json={\n        \"quantity\": 2,\n    },\n)\n\nresponse.raise_for_status()\nprint(response.json())"},{"lang":"javascript","label":"JavaScript","source":"const credential = process.env.REVKEEN_API_KEY;\nif (!credential) throw new Error(\"Set REVKEEN_API_KEY\");\nconst response = await fetch(\"https://api.revkeen.com/v2/cart-sessions/11111111-1111-4111-8111-111111111111/line-items/cli_550e8400-e29b-41d4-a716-446655440000\", {\n  method: \"PATCH\",\n  headers: {\n    \"x-api-key\": credential,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n    \"quantity\": 2\n  }),\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"java","label":"Java","source":"import java.net.URI;\nimport java.net.http.HttpClient;\nimport java.net.http.HttpRequest;\nimport java.net.http.HttpResponse;\n\nHttpClient client = HttpClient.newHttpClient();\n\nHttpRequest request = HttpRequest.newBuilder()\n    .uri(URI.create(\"https://api.revkeen.com/v2/cart-sessions/11111111-1111-4111-8111-111111111111/line-items/cli_550e8400-e29b-41d4-a716-446655440000\"))\n    .header(\"x-api-key\", System.getenv(\"REVKEEN_API_KEY\"))\n    .header(\"Content-Type\", \"application/json\")\n    .method(\"PATCH\", HttpRequest.BodyPublishers.ofString(\"\"\"\n        {\n          \"quantity\": 2\n        }\n        \"\"\"))\n    .build();\n\nHttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());\nSystem.out.println(response.body());"},{"lang":"ruby","label":"Ruby","source":"require \"net/http\"\nrequire \"json\"\nrequire \"uri\"\n\nuri = URI(\"https://api.revkeen.com/v2/cart-sessions/11111111-1111-4111-8111-111111111111/line-items/cli_550e8400-e29b-41d4-a716-446655440000\")\nhttp = Net::HTTP.new(uri.host, uri.port)\nhttp.use_ssl = true\n\nrequest = Net::HTTP::Patch.new(uri)\nrequest[\"x-api-key\"] = ENV[\"REVKEEN_API_KEY\"]\nrequest[\"Content-Type\"] = \"application/json\"\nrequest.body = {\n  \"quantity\" => 2,\n}.to_json\n\nresponse = http.request(request)\nputs response.body"}],"security":[{"apiKey":["cart:write"]},{"oauth2":["cart:write"]}]},"delete":{"operationId":"cart_sessions_remove_line_item","tags":["Cart Sessions"],"x-fern-sdk-group-name":["checkout","cart-sessions","line-items"],"summary":"Remove a line item from a cart session","description":"Remove a line item from an open cart. Subtotals are recomputed; the updated cart is returned.\n\n---\n\n**Related endpoints**\n\n- `POST /cart-sessions` — Create a cart session\n- `GET /cart-sessions/{id}` — Retrieve a cart session\n- `POST /cart-sessions/{id}/line-items` — Add a line item to a cart session\n- `PATCH /cart-sessions/{id}/line-items/{line_id}` — Update a line item's quantity\n- `POST /cart-sessions/{id}/add-ons` — Toggle an add-on on a cart session\n- `POST /cart-sessions/{id}/discount-code` — Set or clear a cart discount code\n- `POST /cart-sessions/{id}/contact` — Capture customer email + consent on a cart\n- `POST /cart-sessions/{id}/convert` — Convert a cart session into a checkout session\n\n**Common errors**\n\n- `401 authentication_error` — missing, invalid, expired, or revoked credential. Codes: `authentication_failed`, `invalid_api_key`, `expired_api_key`, `api_key_revoked`, `session_invalid`, `merchant_required`. Carries a `WWW-Authenticate: Bearer` challenge.\n- `404 resource_missing` — the referenced resource does not exist or is not visible to your key.\n- `409 conflict` — Idempotency-Key collision with a different body, or a concurrent state-transition conflict.\n\n**Idempotency**\n\nPass an `Idempotency-Key` header (UUID v4 recommended) to make retries safe. Keys are valid for 24 hours; see [the idempotency guide](/docs/fundamentals/idempotency).","parameters":[{"schema":{"type":"string","format":"uuid","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"name":"id","in":"path"},{"schema":{"type":"string","description":"Cart line item id (the `id` field returned on `cart_session.line_items[]`).","example":"cli_550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Cart line item id (the `id` field returned on `cart_session.line_items[]`).","name":"line_id","in":"path"}],"responses":{"200":{"description":"Cart session","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CartSessionResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}},"headers":{"WWW-Authenticate":{"description":"RFC 6750 Bearer authentication challenge","schema":{"type":"string"},"example":"Bearer realm=\"revkeen-api\""}}},"404":{"description":"Not Found — cart or line item not found, or not visible to your key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CartSessionErrorResponse"}}}},"409":{"description":"Conflict — cart is closed, empty (on convert), or has a pending discount code that blocks convert.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CartSessionErrorResponse"}}}},"429":{"description":"Too Many Requests - Rate limit exceeded","headers":{"Retry-After":{"description":"Whole seconds until the caller may retry","schema":{"type":"string"},"example":"60"},"X-RateLimit-Limit":{"description":"Request quota for the current window","schema":{"type":"string"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window","schema":{"type":"string"}},"X-RateLimit-Reset":{"description":"UNIX timestamp in seconds when the window resets","schema":{"type":"string"}}}}},"x-cli-group":"cart-sessions","x-cli-name":"remove-line-item","x-codeSamples":[{"lang":"curl","label":"cURL","source":"curl -X DELETE \"https://api.revkeen.com/v2/cart-sessions/11111111-1111-4111-8111-111111111111/line-items/cli_550e8400-e29b-41d4-a716-446655440000\" \\\n  -H \"x-api-key: $REVKEEN_API_KEY\""},{"lang":"typescript","label":"TypeScript (SDK)","source":"// npm install @revkeen/sdk@1.20260822.1541\nimport { RevKeenClient } from \"@revkeen/sdk\";\nconst apiKey = process.env.REVKEEN_API_KEY;\nif (!apiKey) throw new Error(\"Set REVKEEN_API_KEY\");\nconst client = new RevKeenClient({ apiKey, baseUrl: \"https://api.revkeen.com/v2\" });\nconst result = await client.cart.sessionsRemoveLineItem({\n  \"id\": \"11111111-1111-4111-8111-111111111111\",\n  \"lineId\": \"cli_550e8400-e29b-41d4-a716-446655440000\"\n});\nconsole.log(result);"},{"lang":"go","label":"Go (HTTP)","source":"package main\nimport (\"fmt\"; \"io\"; \"net/http\"; \"os\"; \"strings\"; \"time\")\nfunc main() {\n  req, err := http.NewRequest(\"DELETE\", \"https://api.revkeen.com/v2/cart-sessions/11111111-1111-4111-8111-111111111111/line-items/cli_550e8400-e29b-41d4-a716-446655440000\", strings.NewReader(\"\"))\n  if err != nil { panic(err) }\n  req.Header.Set(\"x-api-key\", os.Getenv(\"REVKEEN_API_KEY\"))\n  req.Header.Set(\"Content-Type\", \"application/json\")\n  client := &http.Client{Timeout: 10 * time.Second}\n  response, err := client.Do(req)\n  if err != nil { panic(err) }\n  defer response.Body.Close()\n  if response.StatusCode >= 400 { panic(fmt.Sprintf(\"RevKeen request failed (%d)\", response.StatusCode)) }\n  data, err := io.ReadAll(response.Body)\n  if err != nil { panic(err) }\n  fmt.Println(string(data))\n}"},{"lang":"php","label":"PHP (HTTP)","source":"<?php\n$request = curl_init(\"https://api.revkeen.com/v2/cart-sessions/11111111-1111-4111-8111-111111111111/line-items/cli_550e8400-e29b-41d4-a716-446655440000\");\ncurl_setopt_array($request, [\n  CURLOPT_CUSTOMREQUEST => \"DELETE\",\n  CURLOPT_RETURNTRANSFER => true,\n  CURLOPT_TIMEOUT => 10,\n  CURLOPT_HTTPHEADER => [\"x-api-key: \" . getenv(\"REVKEEN_API_KEY\"), \"Content-Type: application/json\"],\n]);\n$response = curl_exec($request);\nif ($response === false) throw new RuntimeException(curl_error($request));\n$status = curl_getinfo($request, CURLINFO_HTTP_CODE);\ncurl_close($request);\nif ($status >= 400) throw new RuntimeException(\"RevKeen request failed ($status)\");\necho $response;"},{"lang":"python","label":"Python","source":"import os\n\nimport requests\n\nresponse = requests.delete(\n    \"https://api.revkeen.com/v2/cart-sessions/11111111-1111-4111-8111-111111111111/line-items/cli_550e8400-e29b-41d4-a716-446655440000\",\n    headers={\"x-api-key\": os.environ[\"REVKEEN_API_KEY\"]},\n)\n\nresponse.raise_for_status()\nprint(response.json())"},{"lang":"javascript","label":"JavaScript","source":"const credential = process.env.REVKEEN_API_KEY;\nif (!credential) throw new Error(\"Set REVKEEN_API_KEY\");\nconst response = await fetch(\"https://api.revkeen.com/v2/cart-sessions/11111111-1111-4111-8111-111111111111/line-items/cli_550e8400-e29b-41d4-a716-446655440000\", {\n  method: \"DELETE\",\n  headers: {\n    \"x-api-key\": credential,\n  },\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"java","label":"Java","source":"import java.net.URI;\nimport java.net.http.HttpClient;\nimport java.net.http.HttpRequest;\nimport java.net.http.HttpResponse;\n\nHttpClient client = HttpClient.newHttpClient();\n\nHttpRequest request = HttpRequest.newBuilder()\n    .uri(URI.create(\"https://api.revkeen.com/v2/cart-sessions/11111111-1111-4111-8111-111111111111/line-items/cli_550e8400-e29b-41d4-a716-446655440000\"))\n    .header(\"x-api-key\", System.getenv(\"REVKEEN_API_KEY\"))\n    .method(\"DELETE\", HttpRequest.BodyPublishers.noBody())\n    .build();\n\nHttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());\nSystem.out.println(response.body());"},{"lang":"ruby","label":"Ruby","source":"require \"net/http\"\nrequire \"json\"\nrequire \"uri\"\n\nuri = URI(\"https://api.revkeen.com/v2/cart-sessions/11111111-1111-4111-8111-111111111111/line-items/cli_550e8400-e29b-41d4-a716-446655440000\")\nhttp = Net::HTTP.new(uri.host, uri.port)\nhttp.use_ssl = true\n\nrequest = Net::HTTP::Delete.new(uri)\nrequest[\"x-api-key\"] = ENV[\"REVKEEN_API_KEY\"]\n\nresponse = http.request(request)\nputs response.body"}],"security":[{"apiKey":["cart:write"]},{"oauth2":["cart:write"]}]}},"/cart-sessions/{id}/add-ons":{"post":{"operationId":"cart_sessions_toggle_add_on","tags":["Cart Sessions"],"x-fern-sdk-group-name":["checkout","cart-sessions"],"summary":"Toggle an add-on on a cart session","description":"Add or remove an add-on product from `add_ons_selected`. The call is idempotent: sending the same desired state for an add-on already in that state is a no-op and emits no event.\n\n---\n\n**Related endpoints**\n\n- `POST /cart-sessions` — Create a cart session\n- `GET /cart-sessions/{id}` — Retrieve a cart session\n- `POST /cart-sessions/{id}/line-items` — Add a line item to a cart session\n- `PATCH /cart-sessions/{id}/line-items/{line_id}` — Update a line item's quantity\n- `DELETE /cart-sessions/{id}/line-items/{line_id}` — Remove a line item from a cart session\n- `POST /cart-sessions/{id}/discount-code` — Set or clear a cart discount code\n- `POST /cart-sessions/{id}/contact` — Capture customer email + consent on a cart\n- `POST /cart-sessions/{id}/convert` — Convert a cart session into a checkout session\n\n**Common errors**\n\n- `400 invalid_request` — malformed payload or failed validation.\n- `401 authentication_error` — missing, invalid, expired, or revoked credential. Codes: `authentication_failed`, `invalid_api_key`, `expired_api_key`, `api_key_revoked`, `session_invalid`, `merchant_required`. Carries a `WWW-Authenticate: Bearer` challenge.\n- `404 resource_missing` — the referenced resource does not exist or is not visible to your key.\n- `409 conflict` — Idempotency-Key collision with a different body, or a concurrent state-transition conflict.\n\n**Idempotency**\n\nPass an `Idempotency-Key` header (UUID v4 recommended) to make retries safe. Keys are valid for 24 hours; see [the idempotency guide](/docs/fundamentals/idempotency).","parameters":[{"schema":{"type":"string","format":"uuid","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"name":"id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ToggleCartAddOnInput"}}}},"responses":{"200":{"description":"Cart session","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CartSessionResponse"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CartSessionErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}},"headers":{"WWW-Authenticate":{"description":"RFC 6750 Bearer authentication challenge","schema":{"type":"string"},"example":"Bearer realm=\"revkeen-api\""}}},"404":{"description":"Not Found — cart or line item not found, or not visible to your key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CartSessionErrorResponse"}}}},"409":{"description":"Conflict — cart is closed, empty (on convert), or has a pending discount code that blocks convert.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CartSessionErrorResponse"}}}},"429":{"description":"Too Many Requests - Rate limit exceeded","headers":{"Retry-After":{"description":"Whole seconds until the caller may retry","schema":{"type":"string"},"example":"60"},"X-RateLimit-Limit":{"description":"Request quota for the current window","schema":{"type":"string"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window","schema":{"type":"string"}},"X-RateLimit-Reset":{"description":"UNIX timestamp in seconds when the window resets","schema":{"type":"string"}}}}},"x-cli-group":"cart-sessions","x-cli-name":"toggle-add-on","x-codeSamples":[{"lang":"curl","label":"cURL","source":"curl -X POST \"https://api.revkeen.com/v2/cart-sessions/11111111-1111-4111-8111-111111111111/add-ons\" \\\n  -H \"x-api-key: $REVKEEN_API_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"product_id\": \"11111111-1111-4111-8111-111111111111\",\n    \"selected\": true\n  }'"},{"lang":"typescript","label":"TypeScript (SDK)","source":"// npm install @revkeen/sdk@1.20260822.1541\nimport { RevKeenClient } from \"@revkeen/sdk\";\nconst apiKey = process.env.REVKEEN_API_KEY;\nif (!apiKey) throw new Error(\"Set REVKEEN_API_KEY\");\nconst client = new RevKeenClient({ apiKey, baseUrl: \"https://api.revkeen.com/v2\" });\nconst result = await client.cart.sessionsToggleAddOn(\"11111111-1111-4111-8111-111111111111\", {\n  \"product_id\": \"11111111-1111-4111-8111-111111111111\",\n  \"selected\": true\n});\nconsole.log(result);"},{"lang":"go","label":"Go (HTTP)","source":"package main\nimport (\"fmt\"; \"io\"; \"net/http\"; \"os\"; \"strings\"; \"time\")\nfunc main() {\n  req, err := http.NewRequest(\"POST\", \"https://api.revkeen.com/v2/cart-sessions/11111111-1111-4111-8111-111111111111/add-ons\", strings.NewReader(\"{\\\"product_id\\\":\\\"11111111-1111-4111-8111-111111111111\\\",\\\"selected\\\":true}\"))\n  if err != nil { panic(err) }\n  req.Header.Set(\"x-api-key\", os.Getenv(\"REVKEEN_API_KEY\"))\n  req.Header.Set(\"Content-Type\", \"application/json\")\n  client := &http.Client{Timeout: 10 * time.Second}\n  response, err := client.Do(req)\n  if err != nil { panic(err) }\n  defer response.Body.Close()\n  if response.StatusCode >= 400 { panic(fmt.Sprintf(\"RevKeen request failed (%d)\", response.StatusCode)) }\n  data, err := io.ReadAll(response.Body)\n  if err != nil { panic(err) }\n  fmt.Println(string(data))\n}"},{"lang":"php","label":"PHP (HTTP)","source":"<?php\n$request = curl_init(\"https://api.revkeen.com/v2/cart-sessions/11111111-1111-4111-8111-111111111111/add-ons\");\ncurl_setopt_array($request, [\n  CURLOPT_CUSTOMREQUEST => \"POST\",\n  CURLOPT_RETURNTRANSFER => true,\n  CURLOPT_TIMEOUT => 10,\n  CURLOPT_HTTPHEADER => [\"x-api-key: \" . getenv(\"REVKEEN_API_KEY\"), \"Content-Type: application/json\"],\n  CURLOPT_POSTFIELDS => '{\\\"product_id\\\":\\\"11111111-1111-4111-8111-111111111111\\\",\\\"selected\\\":true}',\n]);\n$response = curl_exec($request);\nif ($response === false) throw new RuntimeException(curl_error($request));\n$status = curl_getinfo($request, CURLINFO_HTTP_CODE);\ncurl_close($request);\nif ($status >= 400) throw new RuntimeException(\"RevKeen request failed ($status)\");\necho $response;"},{"lang":"python","label":"Python","source":"import os\n\nimport requests\n\nresponse = requests.post(\n    \"https://api.revkeen.com/v2/cart-sessions/11111111-1111-4111-8111-111111111111/add-ons\",\n    headers={\"x-api-key\": os.environ[\"REVKEEN_API_KEY\"]},\n    json={\n        \"product_id\": \"11111111-1111-4111-8111-111111111111\",\n        \"selected\": True,\n    },\n)\n\nresponse.raise_for_status()\nprint(response.json())"},{"lang":"javascript","label":"JavaScript","source":"const credential = process.env.REVKEEN_API_KEY;\nif (!credential) throw new Error(\"Set REVKEEN_API_KEY\");\nconst response = await fetch(\"https://api.revkeen.com/v2/cart-sessions/11111111-1111-4111-8111-111111111111/add-ons\", {\n  method: \"POST\",\n  headers: {\n    \"x-api-key\": credential,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n    \"product_id\": \"11111111-1111-4111-8111-111111111111\",\n    \"selected\": true\n  }),\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"java","label":"Java","source":"import java.net.URI;\nimport java.net.http.HttpClient;\nimport java.net.http.HttpRequest;\nimport java.net.http.HttpResponse;\n\nHttpClient client = HttpClient.newHttpClient();\n\nHttpRequest request = HttpRequest.newBuilder()\n    .uri(URI.create(\"https://api.revkeen.com/v2/cart-sessions/11111111-1111-4111-8111-111111111111/add-ons\"))\n    .header(\"x-api-key\", System.getenv(\"REVKEEN_API_KEY\"))\n    .header(\"Content-Type\", \"application/json\")\n    .method(\"POST\", HttpRequest.BodyPublishers.ofString(\"\"\"\n        {\n          \"product_id\": \"11111111-1111-4111-8111-111111111111\",\n          \"selected\": true\n        }\n        \"\"\"))\n    .build();\n\nHttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());\nSystem.out.println(response.body());"},{"lang":"ruby","label":"Ruby","source":"require \"net/http\"\nrequire \"json\"\nrequire \"uri\"\n\nuri = URI(\"https://api.revkeen.com/v2/cart-sessions/11111111-1111-4111-8111-111111111111/add-ons\")\nhttp = Net::HTTP.new(uri.host, uri.port)\nhttp.use_ssl = true\n\nrequest = Net::HTTP::Post.new(uri)\nrequest[\"x-api-key\"] = ENV[\"REVKEEN_API_KEY\"]\nrequest[\"Content-Type\"] = \"application/json\"\nrequest.body = {\n  \"product_id\" => \"11111111-1111-4111-8111-111111111111\",\n  \"selected\" => true,\n}.to_json\n\nresponse = http.request(request)\nputs response.body"}],"security":[{"apiKey":["cart:write"]},{"oauth2":["cart:write"]}]}},"/cart-sessions/{id}/discount-code":{"post":{"operationId":"cart_sessions_apply_discount_code","tags":["Cart Sessions"],"x-fern-sdk-group-name":["checkout","cart-sessions"],"summary":"Set or clear a cart discount code","description":"Set the cart's discount code. Pass `null` to clear. The code is validated against the merchant's active discounts and priced into the cart total immediately; convert then carries the discounted amount.\n\n---\n\n**Related endpoints**\n\n- `POST /cart-sessions` — Create a cart session\n- `GET /cart-sessions/{id}` — Retrieve a cart session\n- `POST /cart-sessions/{id}/line-items` — Add a line item to a cart session\n- `PATCH /cart-sessions/{id}/line-items/{line_id}` — Update a line item's quantity\n- `DELETE /cart-sessions/{id}/line-items/{line_id}` — Remove a line item from a cart session\n- `POST /cart-sessions/{id}/add-ons` — Toggle an add-on on a cart session\n- `POST /cart-sessions/{id}/contact` — Capture customer email + consent on a cart\n- `POST /cart-sessions/{id}/convert` — Convert a cart session into a checkout session\n\n**Common errors**\n\n- `400 invalid_request` — malformed payload or failed validation.\n- `401 authentication_error` — missing, invalid, expired, or revoked credential. Codes: `authentication_failed`, `invalid_api_key`, `expired_api_key`, `api_key_revoked`, `session_invalid`, `merchant_required`. Carries a `WWW-Authenticate: Bearer` challenge.\n- `404 resource_missing` — the referenced resource does not exist or is not visible to your key.\n- `409 conflict` — Idempotency-Key collision with a different body, or a concurrent state-transition conflict.\n\n**Idempotency**\n\nPass an `Idempotency-Key` header (UUID v4 recommended) to make retries safe. Keys are valid for 24 hours; see [the idempotency guide](/docs/fundamentals/idempotency).","parameters":[{"schema":{"type":"string","format":"uuid","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"name":"id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApplyCartDiscountCodeInput"}}}},"responses":{"200":{"description":"Cart session","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CartSessionResponse"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CartSessionErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}},"headers":{"WWW-Authenticate":{"description":"RFC 6750 Bearer authentication challenge","schema":{"type":"string"},"example":"Bearer realm=\"revkeen-api\""}}},"404":{"description":"Not Found — cart or line item not found, or not visible to your key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CartSessionErrorResponse"}}}},"409":{"description":"Conflict — cart is closed, empty (on convert), or has a pending discount code that blocks convert.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CartSessionErrorResponse"}}}},"429":{"description":"Too Many Requests - Rate limit exceeded","headers":{"Retry-After":{"description":"Whole seconds until the caller may retry","schema":{"type":"string"},"example":"60"},"X-RateLimit-Limit":{"description":"Request quota for the current window","schema":{"type":"string"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window","schema":{"type":"string"}},"X-RateLimit-Reset":{"description":"UNIX timestamp in seconds when the window resets","schema":{"type":"string"}}}}},"x-cli-group":"cart-sessions","x-cli-name":"apply-discount-code","x-codeSamples":[{"lang":"curl","label":"cURL","source":"curl -X POST \"https://api.revkeen.com/v2/cart-sessions/11111111-1111-4111-8111-111111111111/discount-code\" \\\n  -H \"x-api-key: $REVKEEN_API_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"code\": \"SAVE10\"\n  }'"},{"lang":"typescript","label":"TypeScript (SDK)","source":"// npm install @revkeen/sdk@1.20260822.1541\nimport { RevKeenClient } from \"@revkeen/sdk\";\nconst apiKey = process.env.REVKEEN_API_KEY;\nif (!apiKey) throw new Error(\"Set REVKEEN_API_KEY\");\nconst client = new RevKeenClient({ apiKey, baseUrl: \"https://api.revkeen.com/v2\" });\nconst result = await client.cart.sessionsApplyDiscountCode(\"11111111-1111-4111-8111-111111111111\", {\n  \"code\": \"SAVE10\"\n});\nconsole.log(result);"},{"lang":"go","label":"Go (HTTP)","source":"package main\nimport (\"fmt\"; \"io\"; \"net/http\"; \"os\"; \"strings\"; \"time\")\nfunc main() {\n  req, err := http.NewRequest(\"POST\", \"https://api.revkeen.com/v2/cart-sessions/11111111-1111-4111-8111-111111111111/discount-code\", strings.NewReader(\"{\\\"code\\\":\\\"SAVE10\\\"}\"))\n  if err != nil { panic(err) }\n  req.Header.Set(\"x-api-key\", os.Getenv(\"REVKEEN_API_KEY\"))\n  req.Header.Set(\"Content-Type\", \"application/json\")\n  client := &http.Client{Timeout: 10 * time.Second}\n  response, err := client.Do(req)\n  if err != nil { panic(err) }\n  defer response.Body.Close()\n  if response.StatusCode >= 400 { panic(fmt.Sprintf(\"RevKeen request failed (%d)\", response.StatusCode)) }\n  data, err := io.ReadAll(response.Body)\n  if err != nil { panic(err) }\n  fmt.Println(string(data))\n}"},{"lang":"php","label":"PHP (HTTP)","source":"<?php\n$request = curl_init(\"https://api.revkeen.com/v2/cart-sessions/11111111-1111-4111-8111-111111111111/discount-code\");\ncurl_setopt_array($request, [\n  CURLOPT_CUSTOMREQUEST => \"POST\",\n  CURLOPT_RETURNTRANSFER => true,\n  CURLOPT_TIMEOUT => 10,\n  CURLOPT_HTTPHEADER => [\"x-api-key: \" . getenv(\"REVKEEN_API_KEY\"), \"Content-Type: application/json\"],\n  CURLOPT_POSTFIELDS => '{\\\"code\\\":\\\"SAVE10\\\"}',\n]);\n$response = curl_exec($request);\nif ($response === false) throw new RuntimeException(curl_error($request));\n$status = curl_getinfo($request, CURLINFO_HTTP_CODE);\ncurl_close($request);\nif ($status >= 400) throw new RuntimeException(\"RevKeen request failed ($status)\");\necho $response;"},{"lang":"python","label":"Python","source":"import os\n\nimport requests\n\nresponse = requests.post(\n    \"https://api.revkeen.com/v2/cart-sessions/11111111-1111-4111-8111-111111111111/discount-code\",\n    headers={\"x-api-key\": os.environ[\"REVKEEN_API_KEY\"]},\n    json={\n        \"code\": \"SAVE10\",\n    },\n)\n\nresponse.raise_for_status()\nprint(response.json())"},{"lang":"javascript","label":"JavaScript","source":"const credential = process.env.REVKEEN_API_KEY;\nif (!credential) throw new Error(\"Set REVKEEN_API_KEY\");\nconst response = await fetch(\"https://api.revkeen.com/v2/cart-sessions/11111111-1111-4111-8111-111111111111/discount-code\", {\n  method: \"POST\",\n  headers: {\n    \"x-api-key\": credential,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n    \"code\": \"SAVE10\"\n  }),\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"java","label":"Java","source":"import java.net.URI;\nimport java.net.http.HttpClient;\nimport java.net.http.HttpRequest;\nimport java.net.http.HttpResponse;\n\nHttpClient client = HttpClient.newHttpClient();\n\nHttpRequest request = HttpRequest.newBuilder()\n    .uri(URI.create(\"https://api.revkeen.com/v2/cart-sessions/11111111-1111-4111-8111-111111111111/discount-code\"))\n    .header(\"x-api-key\", System.getenv(\"REVKEEN_API_KEY\"))\n    .header(\"Content-Type\", \"application/json\")\n    .method(\"POST\", HttpRequest.BodyPublishers.ofString(\"\"\"\n        {\n          \"code\": \"SAVE10\"\n        }\n        \"\"\"))\n    .build();\n\nHttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());\nSystem.out.println(response.body());"},{"lang":"ruby","label":"Ruby","source":"require \"net/http\"\nrequire \"json\"\nrequire \"uri\"\n\nuri = URI(\"https://api.revkeen.com/v2/cart-sessions/11111111-1111-4111-8111-111111111111/discount-code\")\nhttp = Net::HTTP.new(uri.host, uri.port)\nhttp.use_ssl = true\n\nrequest = Net::HTTP::Post.new(uri)\nrequest[\"x-api-key\"] = ENV[\"REVKEEN_API_KEY\"]\nrequest[\"Content-Type\"] = \"application/json\"\nrequest.body = {\n  \"code\" => \"SAVE10\",\n}.to_json\n\nresponse = http.request(request)\nputs response.body"}],"security":[{"apiKey":["cart:write"]},{"oauth2":["cart:write"]}]}},"/cart-sessions/{id}/contact":{"post":{"operationId":"cart_sessions_set_contact","tags":["Cart Sessions"],"x-fern-sdk-group-name":["checkout","cart-sessions"],"summary":"Capture customer email + consent on a cart","description":"Capture the customer's email and marketing/recovery consent on an open cart so an abandoned cart can be recovered. Every field is optional; pass `null` to clear. Setting a field records its consent/capture timestamp. Idempotent — an input that changes nothing is a no-op.\n\n---\n\n**Related endpoints**\n\n- `POST /cart-sessions` — Create a cart session\n- `GET /cart-sessions/{id}` — Retrieve a cart session\n- `POST /cart-sessions/{id}/line-items` — Add a line item to a cart session\n- `PATCH /cart-sessions/{id}/line-items/{line_id}` — Update a line item's quantity\n- `DELETE /cart-sessions/{id}/line-items/{line_id}` — Remove a line item from a cart session\n- `POST /cart-sessions/{id}/add-ons` — Toggle an add-on on a cart session\n- `POST /cart-sessions/{id}/discount-code` — Set or clear a cart discount code\n- `POST /cart-sessions/{id}/convert` — Convert a cart session into a checkout session\n\n**Common errors**\n\n- `400 invalid_request` — malformed payload or failed validation.\n- `401 authentication_error` — missing, invalid, expired, or revoked credential. Codes: `authentication_failed`, `invalid_api_key`, `expired_api_key`, `api_key_revoked`, `session_invalid`, `merchant_required`. Carries a `WWW-Authenticate: Bearer` challenge.\n- `404 resource_missing` — the referenced resource does not exist or is not visible to your key.\n- `409 conflict` — Idempotency-Key collision with a different body, or a concurrent state-transition conflict.\n\n**Idempotency**\n\nPass an `Idempotency-Key` header (UUID v4 recommended) to make retries safe. Keys are valid for 24 hours; see [the idempotency guide](/docs/fundamentals/idempotency).","parameters":[{"schema":{"type":"string","format":"uuid","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"name":"id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SetCartContactInput"}}}},"responses":{"200":{"description":"Cart session","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CartSessionResponse"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CartSessionErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}},"headers":{"WWW-Authenticate":{"description":"RFC 6750 Bearer authentication challenge","schema":{"type":"string"},"example":"Bearer realm=\"revkeen-api\""}}},"404":{"description":"Not Found — cart or line item not found, or not visible to your key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CartSessionErrorResponse"}}}},"409":{"description":"Conflict — cart is closed, empty (on convert), or has a pending discount code that blocks convert.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CartSessionErrorResponse"}}}},"429":{"description":"Too Many Requests - Rate limit exceeded","headers":{"Retry-After":{"description":"Whole seconds until the caller may retry","schema":{"type":"string"},"example":"60"},"X-RateLimit-Limit":{"description":"Request quota for the current window","schema":{"type":"string"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window","schema":{"type":"string"}},"X-RateLimit-Reset":{"description":"UNIX timestamp in seconds when the window resets","schema":{"type":"string"}}}}},"x-cli-group":"cart-sessions","x-cli-name":"set-contact","x-codeSamples":[{"lang":"curl","label":"cURL","source":"curl -X POST \"https://api.revkeen.com/v2/cart-sessions/11111111-1111-4111-8111-111111111111/contact\" \\\n  -H \"x-api-key: $REVKEEN_API_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"email\": \"buyer@example.com\",\n    \"promotional_consent\": true,\n    \"sms_consent\": true\n  }'"},{"lang":"typescript","label":"TypeScript (SDK)","source":"// npm install @revkeen/sdk@1.20260822.1541\nimport { RevKeenClient } from \"@revkeen/sdk\";\nconst apiKey = process.env.REVKEEN_API_KEY;\nif (!apiKey) throw new Error(\"Set REVKEEN_API_KEY\");\nconst client = new RevKeenClient({ apiKey, baseUrl: \"https://api.revkeen.com/v2\" });\nconst result = await client.cart.sessionsSetContact(\"11111111-1111-4111-8111-111111111111\", {\n  \"email\": \"buyer@example.com\",\n  \"promotional_consent\": true,\n  \"sms_consent\": true\n});\nconsole.log(result);"},{"lang":"go","label":"Go (HTTP)","source":"package main\nimport (\"fmt\"; \"io\"; \"net/http\"; \"os\"; \"strings\"; \"time\")\nfunc main() {\n  req, err := http.NewRequest(\"POST\", \"https://api.revkeen.com/v2/cart-sessions/11111111-1111-4111-8111-111111111111/contact\", strings.NewReader(\"{\\\"email\\\":\\\"buyer@example.com\\\",\\\"promotional_consent\\\":true,\\\"sms_consent\\\":true}\"))\n  if err != nil { panic(err) }\n  req.Header.Set(\"x-api-key\", os.Getenv(\"REVKEEN_API_KEY\"))\n  req.Header.Set(\"Content-Type\", \"application/json\")\n  client := &http.Client{Timeout: 10 * time.Second}\n  response, err := client.Do(req)\n  if err != nil { panic(err) }\n  defer response.Body.Close()\n  if response.StatusCode >= 400 { panic(fmt.Sprintf(\"RevKeen request failed (%d)\", response.StatusCode)) }\n  data, err := io.ReadAll(response.Body)\n  if err != nil { panic(err) }\n  fmt.Println(string(data))\n}"},{"lang":"php","label":"PHP (HTTP)","source":"<?php\n$request = curl_init(\"https://api.revkeen.com/v2/cart-sessions/11111111-1111-4111-8111-111111111111/contact\");\ncurl_setopt_array($request, [\n  CURLOPT_CUSTOMREQUEST => \"POST\",\n  CURLOPT_RETURNTRANSFER => true,\n  CURLOPT_TIMEOUT => 10,\n  CURLOPT_HTTPHEADER => [\"x-api-key: \" . getenv(\"REVKEEN_API_KEY\"), \"Content-Type: application/json\"],\n  CURLOPT_POSTFIELDS => '{\\\"email\\\":\\\"buyer@example.com\\\",\\\"promotional_consent\\\":true,\\\"sms_consent\\\":true}',\n]);\n$response = curl_exec($request);\nif ($response === false) throw new RuntimeException(curl_error($request));\n$status = curl_getinfo($request, CURLINFO_HTTP_CODE);\ncurl_close($request);\nif ($status >= 400) throw new RuntimeException(\"RevKeen request failed ($status)\");\necho $response;"},{"lang":"python","label":"Python","source":"import os\n\nimport requests\n\nresponse = requests.post(\n    \"https://api.revkeen.com/v2/cart-sessions/11111111-1111-4111-8111-111111111111/contact\",\n    headers={\"x-api-key\": os.environ[\"REVKEEN_API_KEY\"]},\n    json={\n        \"email\": \"buyer@example.com\",\n        \"promotional_consent\": True,\n        \"sms_consent\": True,\n    },\n)\n\nresponse.raise_for_status()\nprint(response.json())"},{"lang":"javascript","label":"JavaScript","source":"const credential = process.env.REVKEEN_API_KEY;\nif (!credential) throw new Error(\"Set REVKEEN_API_KEY\");\nconst response = await fetch(\"https://api.revkeen.com/v2/cart-sessions/11111111-1111-4111-8111-111111111111/contact\", {\n  method: \"POST\",\n  headers: {\n    \"x-api-key\": credential,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n    \"email\": \"buyer@example.com\",\n    \"promotional_consent\": true,\n    \"sms_consent\": true\n  }),\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"java","label":"Java","source":"import java.net.URI;\nimport java.net.http.HttpClient;\nimport java.net.http.HttpRequest;\nimport java.net.http.HttpResponse;\n\nHttpClient client = HttpClient.newHttpClient();\n\nHttpRequest request = HttpRequest.newBuilder()\n    .uri(URI.create(\"https://api.revkeen.com/v2/cart-sessions/11111111-1111-4111-8111-111111111111/contact\"))\n    .header(\"x-api-key\", System.getenv(\"REVKEEN_API_KEY\"))\n    .header(\"Content-Type\", \"application/json\")\n    .method(\"POST\", HttpRequest.BodyPublishers.ofString(\"\"\"\n        {\n          \"email\": \"buyer@example.com\",\n          \"promotional_consent\": true,\n          \"sms_consent\": true\n        }\n        \"\"\"))\n    .build();\n\nHttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());\nSystem.out.println(response.body());"},{"lang":"ruby","label":"Ruby","source":"require \"net/http\"\nrequire \"json\"\nrequire \"uri\"\n\nuri = URI(\"https://api.revkeen.com/v2/cart-sessions/11111111-1111-4111-8111-111111111111/contact\")\nhttp = Net::HTTP.new(uri.host, uri.port)\nhttp.use_ssl = true\n\nrequest = Net::HTTP::Post.new(uri)\nrequest[\"x-api-key\"] = ENV[\"REVKEEN_API_KEY\"]\nrequest[\"Content-Type\"] = \"application/json\"\nrequest.body = {\n  \"email\" => \"buyer@example.com\",\n  \"promotional_consent\" => true,\n  \"sms_consent\" => true,\n}.to_json\n\nresponse = http.request(request)\nputs response.body"}],"security":[{"apiKey":["cart:write"]},{"oauth2":["cart:write"]}]}},"/cart-sessions/{id}/convert":{"post":{"operationId":"cart_sessions_convert","tags":["Cart Sessions"],"x-fern-sdk-group-name":["checkout","cart-sessions"],"summary":"Convert a cart session into a checkout session","description":"Atomically materialise a pending checkout session from the cart snapshot, reserve tracked availability, flip the cart to `converted`, and emit `commerce.cart.converted` + `commerce.checkout.started` through the outbox.\n\nIdempotent on re-call (returns the existing checkout session). Concurrency-safe via a compare-and-swap lock on cart status; product availability is guarded by the database reserve step.\n\nValidation that runs inside the lock and rolls back on failure:\n- `CART_SESSION_EMPTY` — the cart has no line items.\n- `AVAILABILITY_EXCEEDED` — a tracked/capacity-limited product can no longer satisfy the requested quantity.\n- `CART_SESSION_NOT_FOUND` — no cart for this id under the calling merchant.\n- `CART_SESSION_CLOSED` — the cart is already `abandoned` or `expired`.\n\nOn rollback the cart returns to `open` and the customer can retry after fixing the cause.\n\n---\n\n**Related endpoints**\n\n- `POST /cart-sessions` — Create a cart session\n- `GET /cart-sessions/{id}` — Retrieve a cart session\n- `POST /cart-sessions/{id}/line-items` — Add a line item to a cart session\n- `PATCH /cart-sessions/{id}/line-items/{line_id}` — Update a line item's quantity\n- `DELETE /cart-sessions/{id}/line-items/{line_id}` — Remove a line item from a cart session\n- `POST /cart-sessions/{id}/add-ons` — Toggle an add-on on a cart session\n- `POST /cart-sessions/{id}/discount-code` — Set or clear a cart discount code\n- `POST /cart-sessions/{id}/contact` — Capture customer email + consent on a cart\n\n**Common errors**\n\n- `401 authentication_error` — missing, invalid, expired, or revoked credential. Codes: `authentication_failed`, `invalid_api_key`, `expired_api_key`, `api_key_revoked`, `session_invalid`, `merchant_required`. Carries a `WWW-Authenticate: Bearer` challenge.\n- `404 resource_missing` — the referenced resource does not exist or is not visible to your key.\n- `409 conflict` — Idempotency-Key collision with a different body, or a concurrent state-transition conflict.\n\n**Idempotency**\n\nPass an `Idempotency-Key` header (UUID v4 recommended) to make retries safe. Keys are valid for 24 hours; see [the idempotency guide](/docs/fundamentals/idempotency).","parameters":[{"schema":{"type":"string","format":"uuid","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"name":"id","in":"path"}],"responses":{"201":{"description":"Cart converted; updated cart + new checkout session returned","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CartConversionResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}},"headers":{"WWW-Authenticate":{"description":"RFC 6750 Bearer authentication challenge","schema":{"type":"string"},"example":"Bearer realm=\"revkeen-api\""}}},"404":{"description":"Not Found — cart or line item not found, or not visible to your key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CartSessionErrorResponse"}}}},"409":{"description":"Conflict — cart is closed, empty (on convert), or has a pending discount code that blocks convert.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CartSessionErrorResponse"}}}},"429":{"description":"Too Many Requests - Rate limit exceeded","headers":{"Retry-After":{"description":"Whole seconds until the caller may retry","schema":{"type":"string"},"example":"60"},"X-RateLimit-Limit":{"description":"Request quota for the current window","schema":{"type":"string"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window","schema":{"type":"string"}},"X-RateLimit-Reset":{"description":"UNIX timestamp in seconds when the window resets","schema":{"type":"string"}}}},"503":{"description":"No payment method available — the merchant has no online payment method enabled and live for this checkout, or none valid in the requested currency. `code` is `NO_PAYMENT_METHODS_AVAILABLE`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NoPaymentMethodsAvailableError"}}}}},"x-cli-group":"cart-sessions","x-cli-name":"convert","x-codeSamples":[{"lang":"curl","label":"cURL","source":"curl -X POST \"https://api.revkeen.com/v2/cart-sessions/11111111-1111-4111-8111-111111111111/convert\" \\\n  -H \"x-api-key: $REVKEEN_API_KEY\""},{"lang":"typescript","label":"TypeScript (SDK)","source":"// npm install @revkeen/sdk@1.20260822.1541\nimport { RevKeenClient } from \"@revkeen/sdk\";\nconst apiKey = process.env.REVKEEN_API_KEY;\nif (!apiKey) throw new Error(\"Set REVKEEN_API_KEY\");\nconst client = new RevKeenClient({ apiKey, baseUrl: \"https://api.revkeen.com/v2\" });\nconst result = await client.cart.sessionsConvert(\"11111111-1111-4111-8111-111111111111\");\nconsole.log(result);"},{"lang":"go","label":"Go (HTTP)","source":"package main\nimport (\"fmt\"; \"io\"; \"net/http\"; \"os\"; \"strings\"; \"time\")\nfunc main() {\n  req, err := http.NewRequest(\"POST\", \"https://api.revkeen.com/v2/cart-sessions/11111111-1111-4111-8111-111111111111/convert\", strings.NewReader(\"\"))\n  if err != nil { panic(err) }\n  req.Header.Set(\"x-api-key\", os.Getenv(\"REVKEEN_API_KEY\"))\n  req.Header.Set(\"Content-Type\", \"application/json\")\n  client := &http.Client{Timeout: 10 * time.Second}\n  response, err := client.Do(req)\n  if err != nil { panic(err) }\n  defer response.Body.Close()\n  if response.StatusCode >= 400 { panic(fmt.Sprintf(\"RevKeen request failed (%d)\", response.StatusCode)) }\n  data, err := io.ReadAll(response.Body)\n  if err != nil { panic(err) }\n  fmt.Println(string(data))\n}"},{"lang":"php","label":"PHP (HTTP)","source":"<?php\n$request = curl_init(\"https://api.revkeen.com/v2/cart-sessions/11111111-1111-4111-8111-111111111111/convert\");\ncurl_setopt_array($request, [\n  CURLOPT_CUSTOMREQUEST => \"POST\",\n  CURLOPT_RETURNTRANSFER => true,\n  CURLOPT_TIMEOUT => 10,\n  CURLOPT_HTTPHEADER => [\"x-api-key: \" . getenv(\"REVKEEN_API_KEY\"), \"Content-Type: application/json\"],\n]);\n$response = curl_exec($request);\nif ($response === false) throw new RuntimeException(curl_error($request));\n$status = curl_getinfo($request, CURLINFO_HTTP_CODE);\ncurl_close($request);\nif ($status >= 400) throw new RuntimeException(\"RevKeen request failed ($status)\");\necho $response;"},{"lang":"python","label":"Python","source":"import os\n\nimport requests\n\nresponse = requests.post(\n    \"https://api.revkeen.com/v2/cart-sessions/11111111-1111-4111-8111-111111111111/convert\",\n    headers={\"x-api-key\": os.environ[\"REVKEEN_API_KEY\"]},\n)\n\nresponse.raise_for_status()\nprint(response.json())"},{"lang":"javascript","label":"JavaScript","source":"const credential = process.env.REVKEEN_API_KEY;\nif (!credential) throw new Error(\"Set REVKEEN_API_KEY\");\nconst response = await fetch(\"https://api.revkeen.com/v2/cart-sessions/11111111-1111-4111-8111-111111111111/convert\", {\n  method: \"POST\",\n  headers: {\n    \"x-api-key\": credential,\n  },\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"java","label":"Java","source":"import java.net.URI;\nimport java.net.http.HttpClient;\nimport java.net.http.HttpRequest;\nimport java.net.http.HttpResponse;\n\nHttpClient client = HttpClient.newHttpClient();\n\nHttpRequest request = HttpRequest.newBuilder()\n    .uri(URI.create(\"https://api.revkeen.com/v2/cart-sessions/11111111-1111-4111-8111-111111111111/convert\"))\n    .header(\"x-api-key\", System.getenv(\"REVKEEN_API_KEY\"))\n    .method(\"POST\", HttpRequest.BodyPublishers.noBody())\n    .build();\n\nHttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());\nSystem.out.println(response.body());"},{"lang":"ruby","label":"Ruby","source":"require \"net/http\"\nrequire \"json\"\nrequire \"uri\"\n\nuri = URI(\"https://api.revkeen.com/v2/cart-sessions/11111111-1111-4111-8111-111111111111/convert\")\nhttp = Net::HTTP.new(uri.host, uri.port)\nhttp.use_ssl = true\n\nrequest = Net::HTTP::Post.new(uri)\nrequest[\"x-api-key\"] = ENV[\"REVKEEN_API_KEY\"]\n\nresponse = http.request(request)\nputs response.body"}],"security":[{"apiKey":["cart:write"]},{"oauth2":["cart:write"]}]}},"/storefront/products":{"get":{"operationId":"storefront_products_list","tags":["Storefront"],"x-fern-sdk-group-name":["storefront","products"],"summary":"List storefront products","description":"List active, cart-eligible products with browser-safe display data, active prices, and derived availability. Only products listed on the Storefront are returned (store_visibility visible, or scheduled and due). Intended for publishable-key storefront and CMS-picker use; never returns product metadata or internal fields.\n\n---\n\n**Related endpoints**\n\n- `GET /storefront/products/{product_id}` — Get a storefront product\n- `GET /storefront/origins` — List storefront origins\n- `POST /storefront/origins` — Register a storefront origin\n- `DELETE /storefront/origins/{origin_id}` — Remove a storefront origin\n- `GET /storefront/status` — Get storefront integration status\n\n**Common errors**\n\n- `401 authentication_error` — missing, invalid, expired, or revoked credential. Codes: `authentication_failed`, `invalid_api_key`, `expired_api_key`, `api_key_revoked`, `session_invalid`, `merchant_required`. Carries a `WWW-Authenticate: Bearer` challenge.\n- `403 authorization_error` — the caller is identified but the action is denied. Codes include `insufficient_permissions`, `ip_not_allowed`, `merchant_mismatch`, and `origin_not_allowed`.\n\n**Pagination**\n\nOffset-based with `limit` (default 25, max 100) and `offset`. The response `pagination` block includes `total` and `hasMore`. See [the pagination guide](/docs/fundamentals/pagination) for SDK auto-paging helpers.","parameters":[{"schema":{"type":"integer","minimum":1,"maximum":100,"description":"Maximum products to return (default 50, max 100)."},"required":false,"description":"Maximum products to return (default 50, max 100).","name":"limit","in":"query"},{"schema":{"type":["integer","null"],"minimum":0,"maximum":10000,"description":"Products to skip, for pagination."},"required":false,"description":"Products to skip, for pagination.","name":"offset","in":"query"},{"schema":{"type":"string","minLength":1,"maxLength":80,"description":"Only products in this product collection (id or URL handle)."},"required":false,"description":"Only products in this product collection (id or URL handle).","name":"collection","in":"query"},{"schema":{"type":"string","minLength":1,"maxLength":50,"description":"Only products carrying this tag."},"required":false,"description":"Only products carrying this tag.","name":"tag","in":"query"},{"schema":{"type":"string","minLength":1,"maxLength":100,"description":"Case-insensitive match on product name or description."},"required":false,"description":"Case-insensitive match on product name or description.","name":"search","in":"query"},{"schema":{"type":"string","enum":["featured","newest","name","price_asc","price_desc","best_selling"],"description":"featured (collection order, else newest), newest, name, price_asc, price_desc or best_selling (units sold on paid orders in the last 90 days)."},"required":false,"description":"featured (collection order, else newest), newest, name, price_asc, price_desc or best_selling (units sold on paid orders in the last 90 days).","name":"sort","in":"query"}],"responses":{"200":{"description":"Browser-safe product list","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StorefrontProductListResponse"}}}},"401":{"description":"Unauthorized - Invalid or missing API key","headers":{"WWW-Authenticate":{"schema":{"type":"string","description":"RFC 6750 Bearer authentication challenge","example":"Bearer realm=\"revkeen-api\""},"required":true,"description":"RFC 6750 Bearer authentication challenge"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthenticationError"}}}},"403":{"description":"Cart not enabled for this merchant (CART_DISABLED)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ForbiddenError"}}}},"429":{"description":"Too Many Requests - Rate limit exceeded","headers":{"Retry-After":{"description":"Whole seconds until the caller may retry","schema":{"type":"string"},"example":"60"},"X-RateLimit-Limit":{"description":"Request quota for the current window","schema":{"type":"string"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window","schema":{"type":"string"}},"X-RateLimit-Reset":{"description":"UNIX timestamp in seconds when the window resets","schema":{"type":"string"}}}}},"x-cli-group":"storefront","x-cli-name":"products-list","x-codeSamples":[{"lang":"curl","label":"cURL","source":"curl -X GET \"https://api.revkeen.com/v2/storefront/products?limit=10&offset=1\" \\\n  -H \"x-api-key: $REVKEEN_API_KEY\""},{"lang":"typescript","label":"TypeScript (SDK)","source":"// npm install @revkeen/sdk@1.20260822.1541\nimport { RevKeenClient } from \"@revkeen/sdk\";\nconst apiKey = process.env.REVKEEN_API_KEY;\nif (!apiKey) throw new Error(\"Set REVKEEN_API_KEY\");\nconst client = new RevKeenClient({ apiKey, baseUrl: \"https://api.revkeen.com/v2\" });\nconst result = await client.storefront.productsList({\n  \"limit\": 10\n});\nconsole.log(result);"},{"lang":"go","label":"Go (HTTP)","source":"package main\nimport (\"fmt\"; \"io\"; \"net/http\"; \"os\"; \"strings\"; \"time\")\nfunc main() {\n  req, err := http.NewRequest(\"GET\", \"https://api.revkeen.com/v2/storefront/products?limit=10&offset=1\", strings.NewReader(\"\"))\n  if err != nil { panic(err) }\n  req.Header.Set(\"x-api-key\", os.Getenv(\"REVKEEN_API_KEY\"))\n  req.Header.Set(\"Content-Type\", \"application/json\")\n  client := &http.Client{Timeout: 10 * time.Second}\n  response, err := client.Do(req)\n  if err != nil { panic(err) }\n  defer response.Body.Close()\n  if response.StatusCode >= 400 { panic(fmt.Sprintf(\"RevKeen request failed (%d)\", response.StatusCode)) }\n  data, err := io.ReadAll(response.Body)\n  if err != nil { panic(err) }\n  fmt.Println(string(data))\n}"},{"lang":"php","label":"PHP (HTTP)","source":"<?php\n$request = curl_init(\"https://api.revkeen.com/v2/storefront/products?limit=10&offset=1\");\ncurl_setopt_array($request, [\n  CURLOPT_CUSTOMREQUEST => \"GET\",\n  CURLOPT_RETURNTRANSFER => true,\n  CURLOPT_TIMEOUT => 10,\n  CURLOPT_HTTPHEADER => [\"x-api-key: \" . getenv(\"REVKEEN_API_KEY\"), \"Content-Type: application/json\"],\n]);\n$response = curl_exec($request);\nif ($response === false) throw new RuntimeException(curl_error($request));\n$status = curl_getinfo($request, CURLINFO_HTTP_CODE);\ncurl_close($request);\nif ($status >= 400) throw new RuntimeException(\"RevKeen request failed ($status)\");\necho $response;"},{"lang":"python","label":"Python","source":"import os\n\nimport requests\n\nresponse = requests.get(\n    \"https://api.revkeen.com/v2/storefront/products\",\n    headers={\"x-api-key\": os.environ[\"REVKEEN_API_KEY\"]},\n    params={\n        \"limit\": 10,\n        \"offset\": 1,\n    },\n)\n\nresponse.raise_for_status()\nprint(response.json())"},{"lang":"javascript","label":"JavaScript","source":"const credential = process.env.REVKEEN_API_KEY;\nif (!credential) throw new Error(\"Set REVKEEN_API_KEY\");\nconst response = await fetch(\"https://api.revkeen.com/v2/storefront/products?limit=10&offset=1\", {\n  method: \"GET\",\n  headers: {\n    \"x-api-key\": credential,\n  },\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"java","label":"Java","source":"import java.net.URI;\nimport java.net.http.HttpClient;\nimport java.net.http.HttpRequest;\nimport java.net.http.HttpResponse;\n\nHttpClient client = HttpClient.newHttpClient();\n\nHttpRequest request = HttpRequest.newBuilder()\n    .uri(URI.create(\"https://api.revkeen.com/v2/storefront/products?limit=10&offset=1\"))\n    .header(\"x-api-key\", System.getenv(\"REVKEEN_API_KEY\"))\n    .GET()\n    .build();\n\nHttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());\nSystem.out.println(response.body());"},{"lang":"ruby","label":"Ruby","source":"require \"net/http\"\nrequire \"json\"\nrequire \"uri\"\n\nuri = URI(\"https://api.revkeen.com/v2/storefront/products?limit=10&offset=1\")\nhttp = Net::HTTP.new(uri.host, uri.port)\nhttp.use_ssl = true\n\nrequest = Net::HTTP::Get.new(uri)\nrequest[\"x-api-key\"] = ENV[\"REVKEEN_API_KEY\"]\n\nresponse = http.request(request)\nputs response.body"}],"security":[{"apiKey":["products:read"]},{"oauth2":["products:read"]}]}},"/storefront/products/{product_id}":{"get":{"operationId":"storefront_products_get","tags":["Storefront"],"x-fern-sdk-group-name":["storefront","products"],"summary":"Get a storefront product","description":"Fetch one active, cart-eligible product by UUID, URL handle (slug), or product reference. A UUID also finds a product hidden from the Storefront; a handle or reference only finds listed products, and the handle wins over another product's reference. Same browser-safe projection as the list endpoint.\n\n---\n\n**Related endpoints**\n\n- `GET /storefront/products` — List storefront products\n- `GET /storefront/origins` — List storefront origins\n- `POST /storefront/origins` — Register a storefront origin\n- `DELETE /storefront/origins/{origin_id}` — Remove a storefront origin\n- `GET /storefront/status` — Get storefront integration status\n\n**Common errors**\n\n- `401 authentication_error` — missing, invalid, expired, or revoked credential. Codes: `authentication_failed`, `invalid_api_key`, `expired_api_key`, `api_key_revoked`, `session_invalid`, `merchant_required`. Carries a `WWW-Authenticate: Bearer` challenge.\n- `403 authorization_error` — the caller is identified but the action is denied. Codes include `insufficient_permissions`, `ip_not_allowed`, `merchant_mismatch`, and `origin_not_allowed`.\n- `404 resource_missing` — the referenced resource does not exist or is not visible to your key.","parameters":[{"schema":{"type":"string","description":"Product UUID, URL handle (slug), or merchant product reference."},"required":true,"description":"Product UUID, URL handle (slug), or merchant product reference.","name":"product_id","in":"path"}],"responses":{"200":{"description":"Browser-safe product","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StorefrontProductResponse"}}}},"401":{"description":"Unauthorized - Invalid or missing API key","headers":{"WWW-Authenticate":{"schema":{"type":"string","description":"RFC 6750 Bearer authentication challenge","example":"Bearer realm=\"revkeen-api\""},"required":true,"description":"RFC 6750 Bearer authentication challenge"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthenticationError"}}}},"403":{"description":"Cart not enabled for this merchant (CART_DISABLED)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ForbiddenError"}}}},"404":{"description":"Product not found, inactive, or archived","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StorefrontProductError"}}}},"429":{"description":"Too Many Requests - Rate limit exceeded","headers":{"Retry-After":{"description":"Whole seconds until the caller may retry","schema":{"type":"string"},"example":"60"},"X-RateLimit-Limit":{"description":"Request quota for the current window","schema":{"type":"string"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window","schema":{"type":"string"}},"X-RateLimit-Reset":{"description":"UNIX timestamp in seconds when the window resets","schema":{"type":"string"}}}}},"x-cli-group":"storefront","x-cli-name":"products-get","x-codeSamples":[{"lang":"curl","label":"cURL","source":"curl -X GET \"https://api.revkeen.com/v2/storefront/products/prd_1RxKm4MnBoCpDqErFsGtHuIv\" \\\n  -H \"x-api-key: $REVKEEN_API_KEY\""},{"lang":"typescript","label":"TypeScript (SDK)","source":"// npm install @revkeen/sdk@1.20260822.1541\nimport { RevKeenClient } from \"@revkeen/sdk\";\nconst apiKey = process.env.REVKEEN_API_KEY;\nif (!apiKey) throw new Error(\"Set REVKEEN_API_KEY\");\nconst client = new RevKeenClient({ apiKey, baseUrl: \"https://api.revkeen.com/v2\" });\nconst result = await client.storefront.productsGet(\"prd_1RxKm4MnBoCpDqErFsGtHuIv\");\nconsole.log(result);"},{"lang":"go","label":"Go (HTTP)","source":"package main\nimport (\"fmt\"; \"io\"; \"net/http\"; \"os\"; \"strings\"; \"time\")\nfunc main() {\n  req, err := http.NewRequest(\"GET\", \"https://api.revkeen.com/v2/storefront/products/prd_1RxKm4MnBoCpDqErFsGtHuIv\", strings.NewReader(\"\"))\n  if err != nil { panic(err) }\n  req.Header.Set(\"x-api-key\", os.Getenv(\"REVKEEN_API_KEY\"))\n  req.Header.Set(\"Content-Type\", \"application/json\")\n  client := &http.Client{Timeout: 10 * time.Second}\n  response, err := client.Do(req)\n  if err != nil { panic(err) }\n  defer response.Body.Close()\n  if response.StatusCode >= 400 { panic(fmt.Sprintf(\"RevKeen request failed (%d)\", response.StatusCode)) }\n  data, err := io.ReadAll(response.Body)\n  if err != nil { panic(err) }\n  fmt.Println(string(data))\n}"},{"lang":"php","label":"PHP (HTTP)","source":"<?php\n$request = curl_init(\"https://api.revkeen.com/v2/storefront/products/prd_1RxKm4MnBoCpDqErFsGtHuIv\");\ncurl_setopt_array($request, [\n  CURLOPT_CUSTOMREQUEST => \"GET\",\n  CURLOPT_RETURNTRANSFER => true,\n  CURLOPT_TIMEOUT => 10,\n  CURLOPT_HTTPHEADER => [\"x-api-key: \" . getenv(\"REVKEEN_API_KEY\"), \"Content-Type: application/json\"],\n]);\n$response = curl_exec($request);\nif ($response === false) throw new RuntimeException(curl_error($request));\n$status = curl_getinfo($request, CURLINFO_HTTP_CODE);\ncurl_close($request);\nif ($status >= 400) throw new RuntimeException(\"RevKeen request failed ($status)\");\necho $response;"},{"lang":"python","label":"Python","source":"import os\n\nimport requests\n\nresponse = requests.get(\n    \"https://api.revkeen.com/v2/storefront/products/prd_1RxKm4MnBoCpDqErFsGtHuIv\",\n    headers={\"x-api-key\": os.environ[\"REVKEEN_API_KEY\"]},\n)\n\nresponse.raise_for_status()\nprint(response.json())"},{"lang":"javascript","label":"JavaScript","source":"const credential = process.env.REVKEEN_API_KEY;\nif (!credential) throw new Error(\"Set REVKEEN_API_KEY\");\nconst response = await fetch(\"https://api.revkeen.com/v2/storefront/products/prd_1RxKm4MnBoCpDqErFsGtHuIv\", {\n  method: \"GET\",\n  headers: {\n    \"x-api-key\": credential,\n  },\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"java","label":"Java","source":"import java.net.URI;\nimport java.net.http.HttpClient;\nimport java.net.http.HttpRequest;\nimport java.net.http.HttpResponse;\n\nHttpClient client = HttpClient.newHttpClient();\n\nHttpRequest request = HttpRequest.newBuilder()\n    .uri(URI.create(\"https://api.revkeen.com/v2/storefront/products/prd_1RxKm4MnBoCpDqErFsGtHuIv\"))\n    .header(\"x-api-key\", System.getenv(\"REVKEEN_API_KEY\"))\n    .GET()\n    .build();\n\nHttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());\nSystem.out.println(response.body());"},{"lang":"ruby","label":"Ruby","source":"require \"net/http\"\nrequire \"json\"\nrequire \"uri\"\n\nuri = URI(\"https://api.revkeen.com/v2/storefront/products/prd_1RxKm4MnBoCpDqErFsGtHuIv\")\nhttp = Net::HTTP.new(uri.host, uri.port)\nhttp.use_ssl = true\n\nrequest = Net::HTTP::Get.new(uri)\nrequest[\"x-api-key\"] = ENV[\"REVKEEN_API_KEY\"]\n\nresponse = http.request(request)\nputs response.body"}],"security":[{"apiKey":["products:read"]},{"oauth2":["products:read"]}]}},"/storefront/origins":{"get":{"operationId":"storefront_origins_list","tags":["Storefront"],"x-fern-sdk-group-name":["storefront","origins"],"summary":"List storefront origins","description":"List the browser origins registered for publishable-key storefront calls.\n\n---\n\n**Related endpoints**\n\n- `GET /storefront/products` — List storefront products\n- `GET /storefront/products/{product_id}` — Get a storefront product\n- `POST /storefront/origins` — Register a storefront origin\n- `DELETE /storefront/origins/{origin_id}` — Remove a storefront origin\n- `GET /storefront/status` — Get storefront integration status\n\n**Common errors**\n\n- `401 authentication_error` — missing, invalid, expired, or revoked credential. Codes: `authentication_failed`, `invalid_api_key`, `expired_api_key`, `api_key_revoked`, `session_invalid`, `merchant_required`. Carries a `WWW-Authenticate: Bearer` challenge.\n- `403 authorization_error` — the caller is identified but the action is denied. Codes include `insufficient_permissions`, `ip_not_allowed`, `merchant_mismatch`, and `origin_not_allowed`.\n\n**Pagination**\n\nOffset-based with `limit` (default 25, max 100) and `offset`. The response `pagination` block includes `total` and `hasMore`. See [the pagination guide](/docs/fundamentals/pagination) for SDK auto-paging helpers.","responses":{"200":{"description":"Registered origins","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StorefrontOriginListResponse"}}}},"401":{"description":"Unauthorized - Invalid or missing API key","headers":{"WWW-Authenticate":{"schema":{"type":"string","description":"RFC 6750 Bearer authentication challenge","example":"Bearer realm=\"revkeen-api\""},"required":true,"description":"RFC 6750 Bearer authentication challenge"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthenticationError"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MerchantActionForbiddenError"}}}},"429":{"description":"Too Many Requests - Rate limit exceeded","headers":{"Retry-After":{"description":"Whole seconds until the caller may retry","schema":{"type":"string"},"example":"60"},"X-RateLimit-Limit":{"description":"Request quota for the current window","schema":{"type":"string"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window","schema":{"type":"string"}},"X-RateLimit-Reset":{"description":"UNIX timestamp in seconds when the window resets","schema":{"type":"string"}}}}},"x-cli-group":"storefront","x-cli-name":"origins-list","x-codeSamples":[{"lang":"curl","label":"cURL","source":"curl -X GET \"https://api.revkeen.com/v2/storefront/origins\" \\\n  -H \"x-api-key: $REVKEEN_API_KEY\""},{"lang":"typescript","label":"TypeScript (SDK)","source":"// npm install @revkeen/sdk@1.20260822.1541\nimport { RevKeenClient } from \"@revkeen/sdk\";\nconst apiKey = process.env.REVKEEN_API_KEY;\nif (!apiKey) throw new Error(\"Set REVKEEN_API_KEY\");\nconst client = new RevKeenClient({ apiKey, baseUrl: \"https://api.revkeen.com/v2\" });\nconst result = await client.storefront.originsList();\nconsole.log(result);"},{"lang":"go","label":"Go (HTTP)","source":"package main\nimport (\"fmt\"; \"io\"; \"net/http\"; \"os\"; \"strings\"; \"time\")\nfunc main() {\n  req, err := http.NewRequest(\"GET\", \"https://api.revkeen.com/v2/storefront/origins\", strings.NewReader(\"\"))\n  if err != nil { panic(err) }\n  req.Header.Set(\"x-api-key\", os.Getenv(\"REVKEEN_API_KEY\"))\n  req.Header.Set(\"Content-Type\", \"application/json\")\n  client := &http.Client{Timeout: 10 * time.Second}\n  response, err := client.Do(req)\n  if err != nil { panic(err) }\n  defer response.Body.Close()\n  if response.StatusCode >= 400 { panic(fmt.Sprintf(\"RevKeen request failed (%d)\", response.StatusCode)) }\n  data, err := io.ReadAll(response.Body)\n  if err != nil { panic(err) }\n  fmt.Println(string(data))\n}"},{"lang":"php","label":"PHP (HTTP)","source":"<?php\n$request = curl_init(\"https://api.revkeen.com/v2/storefront/origins\");\ncurl_setopt_array($request, [\n  CURLOPT_CUSTOMREQUEST => \"GET\",\n  CURLOPT_RETURNTRANSFER => true,\n  CURLOPT_TIMEOUT => 10,\n  CURLOPT_HTTPHEADER => [\"x-api-key: \" . getenv(\"REVKEEN_API_KEY\"), \"Content-Type: application/json\"],\n]);\n$response = curl_exec($request);\nif ($response === false) throw new RuntimeException(curl_error($request));\n$status = curl_getinfo($request, CURLINFO_HTTP_CODE);\ncurl_close($request);\nif ($status >= 400) throw new RuntimeException(\"RevKeen request failed ($status)\");\necho $response;"},{"lang":"python","label":"Python","source":"import os\n\nimport requests\n\nresponse = requests.get(\n    \"https://api.revkeen.com/v2/storefront/origins\",\n    headers={\"x-api-key\": os.environ[\"REVKEEN_API_KEY\"]},\n)\n\nresponse.raise_for_status()\nprint(response.json())"},{"lang":"javascript","label":"JavaScript","source":"const credential = process.env.REVKEEN_API_KEY;\nif (!credential) throw new Error(\"Set REVKEEN_API_KEY\");\nconst response = await fetch(\"https://api.revkeen.com/v2/storefront/origins\", {\n  method: \"GET\",\n  headers: {\n    \"x-api-key\": credential,\n  },\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"java","label":"Java","source":"import java.net.URI;\nimport java.net.http.HttpClient;\nimport java.net.http.HttpRequest;\nimport java.net.http.HttpResponse;\n\nHttpClient client = HttpClient.newHttpClient();\n\nHttpRequest request = HttpRequest.newBuilder()\n    .uri(URI.create(\"https://api.revkeen.com/v2/storefront/origins\"))\n    .header(\"x-api-key\", System.getenv(\"REVKEEN_API_KEY\"))\n    .GET()\n    .build();\n\nHttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());\nSystem.out.println(response.body());"},{"lang":"ruby","label":"Ruby","source":"require \"net/http\"\nrequire \"json\"\nrequire \"uri\"\n\nuri = URI(\"https://api.revkeen.com/v2/storefront/origins\")\nhttp = Net::HTTP.new(uri.host, uri.port)\nhttp.use_ssl = true\n\nrequest = Net::HTTP::Get.new(uri)\nrequest[\"x-api-key\"] = ENV[\"REVKEEN_API_KEY\"]\n\nresponse = http.request(request)\nputs response.body"}],"security":[{"apiKey":["apps:write"]},{"oauth2":["apps:write"]}]},"post":{"operationId":"storefront_origins_create","tags":["Storefront"],"x-fern-sdk-group-name":["storefront","origins"],"summary":"Register a storefront origin","description":"Register an exact web origin (scheme://host[:port]) for publishable-key storefront calls. Wildcards are rejected; http is allowed only for localhost.\n\n---\n\n**Related endpoints**\n\n- `GET /storefront/products` — List storefront products\n- `GET /storefront/products/{product_id}` — Get a storefront product\n- `GET /storefront/origins` — List storefront origins\n- `DELETE /storefront/origins/{origin_id}` — Remove a storefront origin\n- `GET /storefront/status` — Get storefront integration status\n\n**Common errors**\n\n- `400 invalid_request` — malformed payload or failed validation.\n- `401 authentication_error` — missing, invalid, expired, or revoked credential. Codes: `authentication_failed`, `invalid_api_key`, `expired_api_key`, `api_key_revoked`, `session_invalid`, `merchant_required`. Carries a `WWW-Authenticate: Bearer` challenge.\n- `403 authorization_error` — the caller is identified but the action is denied. Codes include `insufficient_permissions`, `ip_not_allowed`, `merchant_mismatch`, and `origin_not_allowed`.\n- `409 conflict` — Idempotency-Key collision with a different body, or a concurrent state-transition conflict.\n\n**Idempotency**\n\nPass an `Idempotency-Key` header (UUID v4 recommended) to make retries safe. Keys are valid for 24 hours; see [the idempotency guide](/docs/fundamentals/idempotency).","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/StorefrontOriginCreateRequest"}}}},"responses":{"201":{"description":"Origin registered","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StorefrontOriginCreateResponse"}}}},"400":{"description":"Invalid origin","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StorefrontOriginError"}}}},"401":{"description":"Unauthorized - Invalid or missing API key","headers":{"WWW-Authenticate":{"schema":{"type":"string","description":"RFC 6750 Bearer authentication challenge","example":"Bearer realm=\"revkeen-api\""},"required":true,"description":"RFC 6750 Bearer authentication challenge"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthenticationError"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MerchantActionForbiddenError"}}}},"409":{"description":"Origin already registered","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StorefrontOriginError"}}}},"429":{"description":"Too Many Requests - Rate limit exceeded","headers":{"Retry-After":{"description":"Whole seconds until the caller may retry","schema":{"type":"string"},"example":"60"},"X-RateLimit-Limit":{"description":"Request quota for the current window","schema":{"type":"string"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window","schema":{"type":"string"}},"X-RateLimit-Reset":{"description":"UNIX timestamp in seconds when the window resets","schema":{"type":"string"}}}}},"x-cli-group":"storefront","x-cli-name":"origins-create","x-codeSamples":[{"lang":"curl","label":"cURL","source":"curl -X POST \"https://api.revkeen.com/v2/storefront/origins\" \\\n  -H \"x-api-key: $REVKEEN_API_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"origin\": \"https://shop.example.com\"\n  }'"},{"lang":"typescript","label":"TypeScript (SDK)","source":"// npm install @revkeen/sdk@1.20260822.1541\nimport { RevKeenClient } from \"@revkeen/sdk\";\nconst apiKey = process.env.REVKEEN_API_KEY;\nif (!apiKey) throw new Error(\"Set REVKEEN_API_KEY\");\nconst client = new RevKeenClient({ apiKey, baseUrl: \"https://api.revkeen.com/v2\" });\nconst result = await client.storefront.originsCreate({\n  \"origin\": \"https://shop.example.com\"\n});\nconsole.log(result);"},{"lang":"go","label":"Go (HTTP)","source":"package main\nimport (\"fmt\"; \"io\"; \"net/http\"; \"os\"; \"strings\"; \"time\")\nfunc main() {\n  req, err := http.NewRequest(\"POST\", \"https://api.revkeen.com/v2/storefront/origins\", strings.NewReader(\"{\\\"origin\\\":\\\"https://shop.example.com\\\"}\"))\n  if err != nil { panic(err) }\n  req.Header.Set(\"x-api-key\", os.Getenv(\"REVKEEN_API_KEY\"))\n  req.Header.Set(\"Content-Type\", \"application/json\")\n  client := &http.Client{Timeout: 10 * time.Second}\n  response, err := client.Do(req)\n  if err != nil { panic(err) }\n  defer response.Body.Close()\n  if response.StatusCode >= 400 { panic(fmt.Sprintf(\"RevKeen request failed (%d)\", response.StatusCode)) }\n  data, err := io.ReadAll(response.Body)\n  if err != nil { panic(err) }\n  fmt.Println(string(data))\n}"},{"lang":"php","label":"PHP (HTTP)","source":"<?php\n$request = curl_init(\"https://api.revkeen.com/v2/storefront/origins\");\ncurl_setopt_array($request, [\n  CURLOPT_CUSTOMREQUEST => \"POST\",\n  CURLOPT_RETURNTRANSFER => true,\n  CURLOPT_TIMEOUT => 10,\n  CURLOPT_HTTPHEADER => [\"x-api-key: \" . getenv(\"REVKEEN_API_KEY\"), \"Content-Type: application/json\"],\n  CURLOPT_POSTFIELDS => '{\\\"origin\\\":\\\"https://shop.example.com\\\"}',\n]);\n$response = curl_exec($request);\nif ($response === false) throw new RuntimeException(curl_error($request));\n$status = curl_getinfo($request, CURLINFO_HTTP_CODE);\ncurl_close($request);\nif ($status >= 400) throw new RuntimeException(\"RevKeen request failed ($status)\");\necho $response;"},{"lang":"python","label":"Python","source":"import os\n\nimport requests\n\nresponse = requests.post(\n    \"https://api.revkeen.com/v2/storefront/origins\",\n    headers={\"x-api-key\": os.environ[\"REVKEEN_API_KEY\"]},\n    json={\n        \"origin\": \"https://shop.example.com\",\n    },\n)\n\nresponse.raise_for_status()\nprint(response.json())"},{"lang":"javascript","label":"JavaScript","source":"const credential = process.env.REVKEEN_API_KEY;\nif (!credential) throw new Error(\"Set REVKEEN_API_KEY\");\nconst response = await fetch(\"https://api.revkeen.com/v2/storefront/origins\", {\n  method: \"POST\",\n  headers: {\n    \"x-api-key\": credential,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n    \"origin\": \"https://shop.example.com\"\n  }),\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"java","label":"Java","source":"import java.net.URI;\nimport java.net.http.HttpClient;\nimport java.net.http.HttpRequest;\nimport java.net.http.HttpResponse;\n\nHttpClient client = HttpClient.newHttpClient();\n\nHttpRequest request = HttpRequest.newBuilder()\n    .uri(URI.create(\"https://api.revkeen.com/v2/storefront/origins\"))\n    .header(\"x-api-key\", System.getenv(\"REVKEEN_API_KEY\"))\n    .header(\"Content-Type\", \"application/json\")\n    .method(\"POST\", HttpRequest.BodyPublishers.ofString(\"\"\"\n        {\n          \"origin\": \"https://shop.example.com\"\n        }\n        \"\"\"))\n    .build();\n\nHttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());\nSystem.out.println(response.body());"},{"lang":"ruby","label":"Ruby","source":"require \"net/http\"\nrequire \"json\"\nrequire \"uri\"\n\nuri = URI(\"https://api.revkeen.com/v2/storefront/origins\")\nhttp = Net::HTTP.new(uri.host, uri.port)\nhttp.use_ssl = true\n\nrequest = Net::HTTP::Post.new(uri)\nrequest[\"x-api-key\"] = ENV[\"REVKEEN_API_KEY\"]\nrequest[\"Content-Type\"] = \"application/json\"\nrequest.body = {\n  \"origin\" => \"https://shop.example.com\",\n}.to_json\n\nresponse = http.request(request)\nputs response.body"}],"security":[{"apiKey":["apps:write"]},{"oauth2":["apps:write"]}]}},"/storefront/origins/{origin_id}":{"delete":{"operationId":"storefront_origins_delete","tags":["Storefront"],"x-fern-sdk-group-name":["storefront","origins"],"summary":"Remove a storefront origin","description":"Remove a registered storefront origin. Browser calls from it fail closed afterwards.\n\n---\n\n**Related endpoints**\n\n- `GET /storefront/products` — List storefront products\n- `GET /storefront/products/{product_id}` — Get a storefront product\n- `GET /storefront/origins` — List storefront origins\n- `POST /storefront/origins` — Register a storefront origin\n- `GET /storefront/status` — Get storefront integration status\n\n**Common errors**\n\n- `401 authentication_error` — missing, invalid, expired, or revoked credential. Codes: `authentication_failed`, `invalid_api_key`, `expired_api_key`, `api_key_revoked`, `session_invalid`, `merchant_required`. Carries a `WWW-Authenticate: Bearer` challenge.\n- `403 authorization_error` — the caller is identified but the action is denied. Codes include `insufficient_permissions`, `ip_not_allowed`, `merchant_mismatch`, and `origin_not_allowed`.\n- `404 resource_missing` — the referenced resource does not exist or is not visible to your key.\n\n**Idempotency**\n\nPass an `Idempotency-Key` header (UUID v4 recommended) to make retries safe. Keys are valid for 24 hours; see [the idempotency guide](/docs/fundamentals/idempotency).","parameters":[{"schema":{"type":"string","format":"uuid","description":"Storefront origin id."},"required":true,"description":"Storefront origin id.","name":"origin_id","in":"path"}],"responses":{"200":{"description":"Origin removed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StorefrontOriginDeleteResponse"}}}},"401":{"description":"Unauthorized - Invalid or missing API key","headers":{"WWW-Authenticate":{"schema":{"type":"string","description":"RFC 6750 Bearer authentication challenge","example":"Bearer realm=\"revkeen-api\""},"required":true,"description":"RFC 6750 Bearer authentication challenge"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthenticationError"}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MerchantActionForbiddenError"}}}},"404":{"description":"Origin not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StorefrontOriginError"}}}},"429":{"description":"Too Many Requests - Rate limit exceeded","headers":{"Retry-After":{"description":"Whole seconds until the caller may retry","schema":{"type":"string"},"example":"60"},"X-RateLimit-Limit":{"description":"Request quota for the current window","schema":{"type":"string"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window","schema":{"type":"string"}},"X-RateLimit-Reset":{"description":"UNIX timestamp in seconds when the window resets","schema":{"type":"string"}}}}},"x-cli-group":"storefront","x-cli-name":"origins-delete","x-codeSamples":[{"lang":"curl","label":"cURL","source":"curl -X DELETE \"https://api.revkeen.com/v2/storefront/origins/11111111-1111-4111-8111-111111111111\" \\\n  -H \"x-api-key: $REVKEEN_API_KEY\""},{"lang":"typescript","label":"TypeScript (SDK)","source":"// npm install @revkeen/sdk@1.20260822.1541\nimport { RevKeenClient } from \"@revkeen/sdk\";\nconst apiKey = process.env.REVKEEN_API_KEY;\nif (!apiKey) throw new Error(\"Set REVKEEN_API_KEY\");\nconst client = new RevKeenClient({ apiKey, baseUrl: \"https://api.revkeen.com/v2\" });\nconst result = await client.storefront.originsDelete(\"11111111-1111-4111-8111-111111111111\");\nconsole.log(result);"},{"lang":"go","label":"Go (HTTP)","source":"package main\nimport (\"fmt\"; \"io\"; \"net/http\"; \"os\"; \"strings\"; \"time\")\nfunc main() {\n  req, err := http.NewRequest(\"DELETE\", \"https://api.revkeen.com/v2/storefront/origins/11111111-1111-4111-8111-111111111111\", strings.NewReader(\"\"))\n  if err != nil { panic(err) }\n  req.Header.Set(\"x-api-key\", os.Getenv(\"REVKEEN_API_KEY\"))\n  req.Header.Set(\"Content-Type\", \"application/json\")\n  client := &http.Client{Timeout: 10 * time.Second}\n  response, err := client.Do(req)\n  if err != nil { panic(err) }\n  defer response.Body.Close()\n  if response.StatusCode >= 400 { panic(fmt.Sprintf(\"RevKeen request failed (%d)\", response.StatusCode)) }\n  data, err := io.ReadAll(response.Body)\n  if err != nil { panic(err) }\n  fmt.Println(string(data))\n}"},{"lang":"php","label":"PHP (HTTP)","source":"<?php\n$request = curl_init(\"https://api.revkeen.com/v2/storefront/origins/11111111-1111-4111-8111-111111111111\");\ncurl_setopt_array($request, [\n  CURLOPT_CUSTOMREQUEST => \"DELETE\",\n  CURLOPT_RETURNTRANSFER => true,\n  CURLOPT_TIMEOUT => 10,\n  CURLOPT_HTTPHEADER => [\"x-api-key: \" . getenv(\"REVKEEN_API_KEY\"), \"Content-Type: application/json\"],\n]);\n$response = curl_exec($request);\nif ($response === false) throw new RuntimeException(curl_error($request));\n$status = curl_getinfo($request, CURLINFO_HTTP_CODE);\ncurl_close($request);\nif ($status >= 400) throw new RuntimeException(\"RevKeen request failed ($status)\");\necho $response;"},{"lang":"python","label":"Python","source":"import os\n\nimport requests\n\nresponse = requests.delete(\n    \"https://api.revkeen.com/v2/storefront/origins/11111111-1111-4111-8111-111111111111\",\n    headers={\"x-api-key\": os.environ[\"REVKEEN_API_KEY\"]},\n)\n\nresponse.raise_for_status()\nprint(response.json())"},{"lang":"javascript","label":"JavaScript","source":"const credential = process.env.REVKEEN_API_KEY;\nif (!credential) throw new Error(\"Set REVKEEN_API_KEY\");\nconst response = await fetch(\"https://api.revkeen.com/v2/storefront/origins/11111111-1111-4111-8111-111111111111\", {\n  method: \"DELETE\",\n  headers: {\n    \"x-api-key\": credential,\n  },\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"java","label":"Java","source":"import java.net.URI;\nimport java.net.http.HttpClient;\nimport java.net.http.HttpRequest;\nimport java.net.http.HttpResponse;\n\nHttpClient client = HttpClient.newHttpClient();\n\nHttpRequest request = HttpRequest.newBuilder()\n    .uri(URI.create(\"https://api.revkeen.com/v2/storefront/origins/11111111-1111-4111-8111-111111111111\"))\n    .header(\"x-api-key\", System.getenv(\"REVKEEN_API_KEY\"))\n    .method(\"DELETE\", HttpRequest.BodyPublishers.noBody())\n    .build();\n\nHttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());\nSystem.out.println(response.body());"},{"lang":"ruby","label":"Ruby","source":"require \"net/http\"\nrequire \"json\"\nrequire \"uri\"\n\nuri = URI(\"https://api.revkeen.com/v2/storefront/origins/11111111-1111-4111-8111-111111111111\")\nhttp = Net::HTTP.new(uri.host, uri.port)\nhttp.use_ssl = true\n\nrequest = Net::HTTP::Delete.new(uri)\nrequest[\"x-api-key\"] = ENV[\"REVKEEN_API_KEY\"]\n\nresponse = http.request(request)\nputs response.body"}],"security":[{"apiKey":["apps:write"]},{"oauth2":["apps:write"]}]}},"/storefront/status":{"get":{"operationId":"storefront_status_get","tags":["Storefront"],"x-fern-sdk-group-name":["storefront","status"],"summary":"Get storefront integration status","description":"Readiness report for the Cart/headless-storefront setup: activation, managed keys, registered origins, product-read readiness, webhook health, and availability tracking. CART_DISABLED / KEYS_MISSING / ORIGIN_MISSING / PRODUCT_READ_UNAVAILABLE are fail-level; WEBHOOK_MISSING / WEBHOOK_UNREACHABLE are warn-level. Never returns key material.\n\n---\n\n**Related endpoints**\n\n- `GET /storefront/products` — List storefront products\n- `GET /storefront/products/{product_id}` — Get a storefront product\n- `GET /storefront/origins` — List storefront origins\n- `POST /storefront/origins` — Register a storefront origin\n- `DELETE /storefront/origins/{origin_id}` — Remove a storefront origin\n\n**Common errors**\n\n- `401 authentication_error` — missing, invalid, expired, or revoked credential. Codes: `authentication_failed`, `invalid_api_key`, `expired_api_key`, `api_key_revoked`, `session_invalid`, `merchant_required`. Carries a `WWW-Authenticate: Bearer` challenge.\n- `403 authorization_error` — the caller is identified but the action is denied. Codes include `insufficient_permissions`, `ip_not_allowed`, `merchant_mismatch`, and `origin_not_allowed`.","responses":{"200":{"description":"Integration status report","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StorefrontStatusResponse"}}}},"401":{"description":"Unauthorized - Invalid or missing API key","headers":{"WWW-Authenticate":{"schema":{"type":"string","description":"RFC 6750 Bearer authentication challenge","example":"Bearer realm=\"revkeen-api\""},"required":true,"description":"RFC 6750 Bearer authentication challenge"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthenticationError"}}}},"403":{"description":"Forbidden - Insufficient permissions","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ForbiddenError"}}}},"429":{"description":"Too Many Requests - Rate limit exceeded","headers":{"Retry-After":{"description":"Whole seconds until the caller may retry","schema":{"type":"string"},"example":"60"},"X-RateLimit-Limit":{"description":"Request quota for the current window","schema":{"type":"string"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window","schema":{"type":"string"}},"X-RateLimit-Reset":{"description":"UNIX timestamp in seconds when the window resets","schema":{"type":"string"}}}}},"x-cli-group":"storefront","x-cli-name":"status-get","x-codeSamples":[{"lang":"curl","label":"cURL","source":"curl -X GET \"https://api.revkeen.com/v2/storefront/status\" \\\n  -H \"x-api-key: $REVKEEN_API_KEY\""},{"lang":"typescript","label":"TypeScript (SDK)","source":"// npm install @revkeen/sdk@1.20260822.1541\nimport { RevKeenClient } from \"@revkeen/sdk\";\nconst apiKey = process.env.REVKEEN_API_KEY;\nif (!apiKey) throw new Error(\"Set REVKEEN_API_KEY\");\nconst client = new RevKeenClient({ apiKey, baseUrl: \"https://api.revkeen.com/v2\" });\nconst result = await client.storefront.statusGet();\nconsole.log(result);"},{"lang":"go","label":"Go (HTTP)","source":"package main\nimport (\"fmt\"; \"io\"; \"net/http\"; \"os\"; \"strings\"; \"time\")\nfunc main() {\n  req, err := http.NewRequest(\"GET\", \"https://api.revkeen.com/v2/storefront/status\", strings.NewReader(\"\"))\n  if err != nil { panic(err) }\n  req.Header.Set(\"x-api-key\", os.Getenv(\"REVKEEN_API_KEY\"))\n  req.Header.Set(\"Content-Type\", \"application/json\")\n  client := &http.Client{Timeout: 10 * time.Second}\n  response, err := client.Do(req)\n  if err != nil { panic(err) }\n  defer response.Body.Close()\n  if response.StatusCode >= 400 { panic(fmt.Sprintf(\"RevKeen request failed (%d)\", response.StatusCode)) }\n  data, err := io.ReadAll(response.Body)\n  if err != nil { panic(err) }\n  fmt.Println(string(data))\n}"},{"lang":"php","label":"PHP (HTTP)","source":"<?php\n$request = curl_init(\"https://api.revkeen.com/v2/storefront/status\");\ncurl_setopt_array($request, [\n  CURLOPT_CUSTOMREQUEST => \"GET\",\n  CURLOPT_RETURNTRANSFER => true,\n  CURLOPT_TIMEOUT => 10,\n  CURLOPT_HTTPHEADER => [\"x-api-key: \" . getenv(\"REVKEEN_API_KEY\"), \"Content-Type: application/json\"],\n]);\n$response = curl_exec($request);\nif ($response === false) throw new RuntimeException(curl_error($request));\n$status = curl_getinfo($request, CURLINFO_HTTP_CODE);\ncurl_close($request);\nif ($status >= 400) throw new RuntimeException(\"RevKeen request failed ($status)\");\necho $response;"},{"lang":"python","label":"Python","source":"import os\n\nimport requests\n\nresponse = requests.get(\n    \"https://api.revkeen.com/v2/storefront/status\",\n    headers={\"x-api-key\": os.environ[\"REVKEEN_API_KEY\"]},\n)\n\nresponse.raise_for_status()\nprint(response.json())"},{"lang":"javascript","label":"JavaScript","source":"const credential = process.env.REVKEEN_API_KEY;\nif (!credential) throw new Error(\"Set REVKEEN_API_KEY\");\nconst response = await fetch(\"https://api.revkeen.com/v2/storefront/status\", {\n  method: \"GET\",\n  headers: {\n    \"x-api-key\": credential,\n  },\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"java","label":"Java","source":"import java.net.URI;\nimport java.net.http.HttpClient;\nimport java.net.http.HttpRequest;\nimport java.net.http.HttpResponse;\n\nHttpClient client = HttpClient.newHttpClient();\n\nHttpRequest request = HttpRequest.newBuilder()\n    .uri(URI.create(\"https://api.revkeen.com/v2/storefront/status\"))\n    .header(\"x-api-key\", System.getenv(\"REVKEEN_API_KEY\"))\n    .GET()\n    .build();\n\nHttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());\nSystem.out.println(response.body());"},{"lang":"ruby","label":"Ruby","source":"require \"net/http\"\nrequire \"json\"\nrequire \"uri\"\n\nuri = URI(\"https://api.revkeen.com/v2/storefront/status\")\nhttp = Net::HTTP.new(uri.host, uri.port)\nhttp.use_ssl = true\n\nrequest = Net::HTTP::Get.new(uri)\nrequest[\"x-api-key\"] = ENV[\"REVKEEN_API_KEY\"]\n\nresponse = http.request(request)\nputs response.body"}],"security":[{"apiKey":["apps:read"]},{"oauth2":["apps:read"]}]}},"/customers/{id}/state":{"get":{"operationId":"customer_state_get","tags":["Customers"],"x-fern-sdk-group-name":["customers"],"x-fern-sdk-method-name":"state","summary":"Get customer billing state","description":"Return the Polar-shaped job snapshot for one customer: identity, active and recent subscriptions, and recent invoices with overdue flags. Use this instead of chaining customers_get + subscriptions_list + invoices_list.\n\nThe snapshot includes subscriptions and invoices, so the key needs `customers:read`, `subscriptions:read` and `invoices:read`; a key missing any of them gets `403`.\n\n---\n\n**Related endpoints**\n\n- `POST /customers` — Create a new customer\n- `GET /customers` — List customers\n- `GET /customers/{id}` — Get customer by ID\n- `PATCH /customers/{id}` — Update customer details\n- `GET /customers/{id}/payment-methods` — Get customer payment methods\n\n**Common errors**\n\n- `401 authentication_error` — missing, invalid, expired, or revoked credential. Codes: `authentication_failed`, `invalid_api_key`, `expired_api_key`, `api_key_revoked`, `session_invalid`, `merchant_required`. Carries a `WWW-Authenticate: Bearer` challenge.\n- `403 authorization_error` — the caller is identified but the action is denied. Codes include `insufficient_permissions`, `ip_not_allowed`, `merchant_mismatch`, and `origin_not_allowed`.\n- `404 resource_missing` — the referenced resource does not exist or is not visible to your key.","parameters":[{"schema":{"type":"string","format":"uuid","description":"Customer UUID"},"required":true,"description":"Customer UUID","name":"id","in":"path"}],"responses":{"200":{"description":"Customer state snapshot","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/CustomerStateResponse"}},"required":["data"]}}}},"401":{"description":"Unauthorized","headers":{"WWW-Authenticate":{"description":"RFC 6750 Bearer authentication challenge","schema":{"type":"string"},"example":"Bearer realm=\"revkeen-api\""}}},"403":{"description":"Forbidden - Insufficient permissions","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ForbiddenError"}}}},"404":{"description":"Customer not found"},"429":{"description":"Too Many Requests - Rate limit exceeded","headers":{"Retry-After":{"description":"Whole seconds until the caller may retry","schema":{"type":"string"},"example":"60"},"X-RateLimit-Limit":{"description":"Request quota for the current window","schema":{"type":"string"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window","schema":{"type":"string"}},"X-RateLimit-Reset":{"description":"UNIX timestamp in seconds when the window resets","schema":{"type":"string"}}}}},"x-cli-group":"customers","x-cli-name":"customer-state-get","x-codeSamples":[{"lang":"curl","label":"cURL","source":"curl -X GET \"https://api.revkeen.com/v2/customers/11111111-1111-4111-8111-111111111111/state\" \\\n  -H \"x-api-key: $REVKEEN_API_KEY\""},{"lang":"typescript","label":"TypeScript (HTTP)","source":"const credential = process.env.REVKEEN_API_KEY;\nif (!credential) throw new Error(\"Set REVKEEN_API_KEY\");\nconst response = await fetch(\"https://api.revkeen.com/v2/customers/11111111-1111-4111-8111-111111111111/state\", {\n  method: \"GET\",\n  headers: {\n    \"x-api-key\": credential,\n  },\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"go","label":"Go (HTTP)","source":"package main\nimport (\"fmt\"; \"io\"; \"net/http\"; \"os\"; \"strings\"; \"time\")\nfunc main() {\n  req, err := http.NewRequest(\"GET\", \"https://api.revkeen.com/v2/customers/11111111-1111-4111-8111-111111111111/state\", strings.NewReader(\"\"))\n  if err != nil { panic(err) }\n  req.Header.Set(\"x-api-key\", os.Getenv(\"REVKEEN_API_KEY\"))\n  req.Header.Set(\"Content-Type\", \"application/json\")\n  client := &http.Client{Timeout: 10 * time.Second}\n  response, err := client.Do(req)\n  if err != nil { panic(err) }\n  defer response.Body.Close()\n  if response.StatusCode >= 400 { panic(fmt.Sprintf(\"RevKeen request failed (%d)\", response.StatusCode)) }\n  data, err := io.ReadAll(response.Body)\n  if err != nil { panic(err) }\n  fmt.Println(string(data))\n}"},{"lang":"php","label":"PHP (HTTP)","source":"<?php\n$request = curl_init(\"https://api.revkeen.com/v2/customers/11111111-1111-4111-8111-111111111111/state\");\ncurl_setopt_array($request, [\n  CURLOPT_CUSTOMREQUEST => \"GET\",\n  CURLOPT_RETURNTRANSFER => true,\n  CURLOPT_TIMEOUT => 10,\n  CURLOPT_HTTPHEADER => [\"x-api-key: \" . getenv(\"REVKEEN_API_KEY\"), \"Content-Type: application/json\"],\n]);\n$response = curl_exec($request);\nif ($response === false) throw new RuntimeException(curl_error($request));\n$status = curl_getinfo($request, CURLINFO_HTTP_CODE);\ncurl_close($request);\nif ($status >= 400) throw new RuntimeException(\"RevKeen request failed ($status)\");\necho $response;"},{"lang":"python","label":"Python","source":"import os\n\nimport requests\n\nresponse = requests.get(\n    \"https://api.revkeen.com/v2/customers/11111111-1111-4111-8111-111111111111/state\",\n    headers={\"x-api-key\": os.environ[\"REVKEEN_API_KEY\"]},\n)\n\nresponse.raise_for_status()\nprint(response.json())"},{"lang":"javascript","label":"JavaScript","source":"const credential = process.env.REVKEEN_API_KEY;\nif (!credential) throw new Error(\"Set REVKEEN_API_KEY\");\nconst response = await fetch(\"https://api.revkeen.com/v2/customers/11111111-1111-4111-8111-111111111111/state\", {\n  method: \"GET\",\n  headers: {\n    \"x-api-key\": credential,\n  },\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"java","label":"Java","source":"import java.net.URI;\nimport java.net.http.HttpClient;\nimport java.net.http.HttpRequest;\nimport java.net.http.HttpResponse;\n\nHttpClient client = HttpClient.newHttpClient();\n\nHttpRequest request = HttpRequest.newBuilder()\n    .uri(URI.create(\"https://api.revkeen.com/v2/customers/11111111-1111-4111-8111-111111111111/state\"))\n    .header(\"x-api-key\", System.getenv(\"REVKEEN_API_KEY\"))\n    .GET()\n    .build();\n\nHttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());\nSystem.out.println(response.body());"},{"lang":"ruby","label":"Ruby","source":"require \"net/http\"\nrequire \"json\"\nrequire \"uri\"\n\nuri = URI(\"https://api.revkeen.com/v2/customers/11111111-1111-4111-8111-111111111111/state\")\nhttp = Net::HTTP.new(uri.host, uri.port)\nhttp.use_ssl = true\n\nrequest = Net::HTTP::Get.new(uri)\nrequest[\"x-api-key\"] = ENV[\"REVKEEN_API_KEY\"]\n\nresponse = http.request(request)\nputs response.body"}],"security":[{"apiKey":["customers:read","invoices:read","subscriptions:read"]},{"oauth2":["customers:read","invoices:read","subscriptions:read"]}]}},"/customers":{"post":{"operationId":"customers_create","tags":["Customers"],"x-fern-sdk-group-name":["customers"],"summary":"Create a new customer","description":"Create a new customer record in the merchant's account\n\n---\n\n**Related endpoints**\n\n- `GET /customers/{id}/state` — Get customer billing state\n- `GET /customers` — List customers\n- `GET /customers/{id}` — Get customer by ID\n- `PATCH /customers/{id}` — Update customer details\n- `GET /customers/{id}/payment-methods` — Get customer payment methods\n\n**Common errors**\n\n- `400 invalid_request` — malformed payload or failed validation.\n- `401 authentication_error` — missing, invalid, expired, or revoked credential. Codes: `authentication_failed`, `invalid_api_key`, `expired_api_key`, `api_key_revoked`, `session_invalid`, `merchant_required`. Carries a `WWW-Authenticate: Bearer` challenge.\n- `409 conflict` — Idempotency-Key collision with a different body, or a concurrent state-transition conflict.\n\n**Idempotency**\n\nPass an `Idempotency-Key` header (UUID v4 recommended) to make retries safe. Keys are valid for 24 hours; see [the idempotency guide](/docs/fundamentals/idempotency).","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"email":{"type":"string","format":"email","description":"Customer's email address (must be unique per merchant)"},"external_id":{"type":"string","minLength":1,"maxLength":255,"description":"Your system's identifier for this customer. Unique per merchant and source: no two customers whose external id you set through the API can share one, but a customer synced from an integration can carry the same value. Use it as external_customer_id when ingesting usage events."},"name":{"type":"string","description":"Customer's full name"},"phone":{"type":"string","description":"Customer's phone number"},"company_name":{"type":"string","description":"Customer's company name"},"address_line1":{"type":"string","description":"Billing address line 1"},"address_line2":{"type":"string","description":"Billing address line 2"},"city":{"type":"string","description":"Billing address city"},"state":{"type":"string","description":"Billing address state or county"},"postal_code":{"type":"string","description":"Billing address postal code"},"country":{"type":"string","description":"Billing address country code"},"metadata":{"type":"object","additionalProperties":true,"description":"Arbitrary key-value metadata to attach to this customer"},"custom_fields":{"type":"object","additionalProperties":true,"description":"Merchant-defined custom fields displayed as columns in the dashboard"}},"required":["email"],"additionalProperties":false}}}},"responses":{"201":{"description":"Customer created successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CustomerCreateResponse"}}}},"400":{"description":"Validation error. `param` names the body field to fix; an unknown field is rejected.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BodyValidationError"}}}},"401":{"description":"Unauthorized","headers":{"WWW-Authenticate":{"description":"RFC 6750 Bearer authentication challenge","schema":{"type":"string"},"example":"Bearer realm=\"revkeen-api\""}}},"409":{"description":"Another customer of this merchant already uses this email address (param email), or another customer whose external id was set through the API already uses this external id (param external_id)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConflictError"}}}},"429":{"description":"Too Many Requests - Rate limit exceeded","headers":{"Retry-After":{"description":"Whole seconds until the caller may retry","schema":{"type":"string"},"example":"60"},"X-RateLimit-Limit":{"description":"Request quota for the current window","schema":{"type":"string"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window","schema":{"type":"string"}},"X-RateLimit-Reset":{"description":"UNIX timestamp in seconds when the window resets","schema":{"type":"string"}}}}},"x-cli-group":"customers","x-cli-name":"create","x-codeSamples":[{"lang":"curl","label":"cURL","source":"curl -X POST \"https://api.revkeen.com/v2/customers\" \\\n  -H \"x-api-key: $REVKEEN_API_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"email\": \"customer@example.com\"\n  }'"},{"lang":"typescript","label":"TypeScript (SDK)","source":"// npm install @revkeen/sdk@1.20260822.1541\nimport { RevKeenClient } from \"@revkeen/sdk\";\nconst apiKey = process.env.REVKEEN_API_KEY;\nif (!apiKey) throw new Error(\"Set REVKEEN_API_KEY\");\nconst client = new RevKeenClient({ apiKey, baseUrl: \"https://api.revkeen.com/v2\" });\nconst result = await client.customers.create({\n  \"email\": \"customer@example.com\"\n});\nconsole.log(result);"},{"lang":"go","label":"Go (HTTP)","source":"package main\nimport (\"fmt\"; \"io\"; \"net/http\"; \"os\"; \"strings\"; \"time\")\nfunc main() {\n  req, err := http.NewRequest(\"POST\", \"https://api.revkeen.com/v2/customers\", strings.NewReader(\"{\\\"email\\\":\\\"customer@example.com\\\"}\"))\n  if err != nil { panic(err) }\n  req.Header.Set(\"x-api-key\", os.Getenv(\"REVKEEN_API_KEY\"))\n  req.Header.Set(\"Content-Type\", \"application/json\")\n  client := &http.Client{Timeout: 10 * time.Second}\n  response, err := client.Do(req)\n  if err != nil { panic(err) }\n  defer response.Body.Close()\n  if response.StatusCode >= 400 { panic(fmt.Sprintf(\"RevKeen request failed (%d)\", response.StatusCode)) }\n  data, err := io.ReadAll(response.Body)\n  if err != nil { panic(err) }\n  fmt.Println(string(data))\n}"},{"lang":"php","label":"PHP (HTTP)","source":"<?php\n$request = curl_init(\"https://api.revkeen.com/v2/customers\");\ncurl_setopt_array($request, [\n  CURLOPT_CUSTOMREQUEST => \"POST\",\n  CURLOPT_RETURNTRANSFER => true,\n  CURLOPT_TIMEOUT => 10,\n  CURLOPT_HTTPHEADER => [\"x-api-key: \" . getenv(\"REVKEEN_API_KEY\"), \"Content-Type: application/json\"],\n  CURLOPT_POSTFIELDS => '{\\\"email\\\":\\\"customer@example.com\\\"}',\n]);\n$response = curl_exec($request);\nif ($response === false) throw new RuntimeException(curl_error($request));\n$status = curl_getinfo($request, CURLINFO_HTTP_CODE);\ncurl_close($request);\nif ($status >= 400) throw new RuntimeException(\"RevKeen request failed ($status)\");\necho $response;"},{"lang":"python","label":"Python","source":"import os\n\nimport requests\n\nresponse = requests.post(\n    \"https://api.revkeen.com/v2/customers\",\n    headers={\"x-api-key\": os.environ[\"REVKEEN_API_KEY\"]},\n    json={\n        \"email\": \"customer@example.com\",\n    },\n)\n\nresponse.raise_for_status()\nprint(response.json())"},{"lang":"javascript","label":"JavaScript","source":"const credential = process.env.REVKEEN_API_KEY;\nif (!credential) throw new Error(\"Set REVKEEN_API_KEY\");\nconst response = await fetch(\"https://api.revkeen.com/v2/customers\", {\n  method: \"POST\",\n  headers: {\n    \"x-api-key\": credential,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n    \"email\": \"customer@example.com\"\n  }),\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"java","label":"Java","source":"import java.net.URI;\nimport java.net.http.HttpClient;\nimport java.net.http.HttpRequest;\nimport java.net.http.HttpResponse;\n\nHttpClient client = HttpClient.newHttpClient();\n\nHttpRequest request = HttpRequest.newBuilder()\n    .uri(URI.create(\"https://api.revkeen.com/v2/customers\"))\n    .header(\"x-api-key\", System.getenv(\"REVKEEN_API_KEY\"))\n    .header(\"Content-Type\", \"application/json\")\n    .method(\"POST\", HttpRequest.BodyPublishers.ofString(\"\"\"\n        {\n          \"email\": \"customer@example.com\"\n        }\n        \"\"\"))\n    .build();\n\nHttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());\nSystem.out.println(response.body());"},{"lang":"ruby","label":"Ruby","source":"require \"net/http\"\nrequire \"json\"\nrequire \"uri\"\n\nuri = URI(\"https://api.revkeen.com/v2/customers\")\nhttp = Net::HTTP.new(uri.host, uri.port)\nhttp.use_ssl = true\n\nrequest = Net::HTTP::Post.new(uri)\nrequest[\"x-api-key\"] = ENV[\"REVKEEN_API_KEY\"]\nrequest[\"Content-Type\"] = \"application/json\"\nrequest.body = {\n  \"email\" => \"customer@example.com\",\n}.to_json\n\nresponse = http.request(request)\nputs response.body"}],"security":[{"apiKey":["customers:write"]},{"oauth2":["customers:write"]}]},"get":{"operationId":"customers_list","tags":["Customers"],"x-fern-sdk-group-name":["customers"],"summary":"List customers","description":"Retrieve a paginated list of customers for the merchant\n\n---\n\n**Related endpoints**\n\n- `GET /customers/{id}/state` — Get customer billing state\n- `POST /customers` — Create a new customer\n- `GET /customers/{id}` — Get customer by ID\n- `PATCH /customers/{id}` — Update customer details\n- `GET /customers/{id}/payment-methods` — Get customer payment methods\n\n**Common errors**\n\n- `401 authentication_error` — missing, invalid, expired, or revoked credential. Codes: `authentication_failed`, `invalid_api_key`, `expired_api_key`, `api_key_revoked`, `session_invalid`, `merchant_required`. Carries a `WWW-Authenticate: Bearer` challenge.\n\n**Pagination**\n\nOffset-based with `limit` (default 25, max 100) and `offset`. The response `pagination` block includes `total` and `hasMore`. See [the pagination guide](/docs/fundamentals/pagination) for SDK auto-paging helpers.","parameters":[{"schema":{"type":"integer","minimum":1,"maximum":100,"default":20,"description":"Maximum number of results (1-100)"},"required":false,"description":"Maximum number of results (1-100)","name":"limit","in":"query"},{"schema":{"type":["integer","null"],"minimum":0,"default":0,"description":"Number of results to skip"},"required":false,"description":"Number of results to skip","name":"offset","in":"query"},{"schema":{"type":"string","description":"Search term to filter customers by name or email. Matched as a literal substring, case-insensitively."},"required":false,"description":"Search term to filter customers by name or email. Matched as a literal substring, case-insensitively.","name":"search","in":"query"},{"schema":{"type":"string","minLength":1,"maxLength":255,"description":"Return the customers whose external_id equals this value. Usually that is one customer; it is more than one only when a customer synced from an integration carries the same value."},"required":false,"description":"Return the customers whose external_id equals this value. Usually that is one customer; it is more than one only when a customer synced from an integration carries the same value.","name":"external_id","in":"query"}],"responses":{"200":{"description":"List of customers","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CustomerListResponse"}}}},"401":{"description":"Unauthorized","headers":{"WWW-Authenticate":{"description":"RFC 6750 Bearer authentication challenge","schema":{"type":"string"},"example":"Bearer realm=\"revkeen-api\""}}},"429":{"description":"Too Many Requests - Rate limit exceeded","headers":{"Retry-After":{"description":"Whole seconds until the caller may retry","schema":{"type":"string"},"example":"60"},"X-RateLimit-Limit":{"description":"Request quota for the current window","schema":{"type":"string"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window","schema":{"type":"string"}},"X-RateLimit-Reset":{"description":"UNIX timestamp in seconds when the window resets","schema":{"type":"string"}}}}},"x-cli-group":"customers","x-cli-name":"list","x-codeSamples":[{"lang":"curl","label":"cURL","source":"curl -X GET \"https://api.revkeen.com/v2/customers?limit=20&offset=0\" \\\n  -H \"x-api-key: $REVKEEN_API_KEY\""},{"lang":"typescript","label":"TypeScript (SDK)","source":"// npm install @revkeen/sdk@1.20260822.1541\nimport { RevKeenClient } from \"@revkeen/sdk\";\nconst apiKey = process.env.REVKEEN_API_KEY;\nif (!apiKey) throw new Error(\"Set REVKEEN_API_KEY\");\nconst client = new RevKeenClient({ apiKey, baseUrl: \"https://api.revkeen.com/v2\" });\nconst result = await client.customers.list({\n  \"limit\": 20,\n  \"offset\": 0\n});\nconsole.log(result);"},{"lang":"go","label":"Go (HTTP)","source":"package main\nimport (\"fmt\"; \"io\"; \"net/http\"; \"os\"; \"strings\"; \"time\")\nfunc main() {\n  req, err := http.NewRequest(\"GET\", \"https://api.revkeen.com/v2/customers?limit=20&offset=0\", strings.NewReader(\"\"))\n  if err != nil { panic(err) }\n  req.Header.Set(\"x-api-key\", os.Getenv(\"REVKEEN_API_KEY\"))\n  req.Header.Set(\"Content-Type\", \"application/json\")\n  client := &http.Client{Timeout: 10 * time.Second}\n  response, err := client.Do(req)\n  if err != nil { panic(err) }\n  defer response.Body.Close()\n  if response.StatusCode >= 400 { panic(fmt.Sprintf(\"RevKeen request failed (%d)\", response.StatusCode)) }\n  data, err := io.ReadAll(response.Body)\n  if err != nil { panic(err) }\n  fmt.Println(string(data))\n}"},{"lang":"php","label":"PHP (HTTP)","source":"<?php\n$request = curl_init(\"https://api.revkeen.com/v2/customers?limit=20&offset=0\");\ncurl_setopt_array($request, [\n  CURLOPT_CUSTOMREQUEST => \"GET\",\n  CURLOPT_RETURNTRANSFER => true,\n  CURLOPT_TIMEOUT => 10,\n  CURLOPT_HTTPHEADER => [\"x-api-key: \" . getenv(\"REVKEEN_API_KEY\"), \"Content-Type: application/json\"],\n]);\n$response = curl_exec($request);\nif ($response === false) throw new RuntimeException(curl_error($request));\n$status = curl_getinfo($request, CURLINFO_HTTP_CODE);\ncurl_close($request);\nif ($status >= 400) throw new RuntimeException(\"RevKeen request failed ($status)\");\necho $response;"},{"lang":"python","label":"Python","source":"import os\n\nimport requests\n\nresponse = requests.get(\n    \"https://api.revkeen.com/v2/customers\",\n    headers={\"x-api-key\": os.environ[\"REVKEEN_API_KEY\"]},\n    params={\n        \"limit\": 20,\n        \"offset\": 0,\n    },\n)\n\nresponse.raise_for_status()\nprint(response.json())"},{"lang":"javascript","label":"JavaScript","source":"const credential = process.env.REVKEEN_API_KEY;\nif (!credential) throw new Error(\"Set REVKEEN_API_KEY\");\nconst response = await fetch(\"https://api.revkeen.com/v2/customers?limit=20&offset=0\", {\n  method: \"GET\",\n  headers: {\n    \"x-api-key\": credential,\n  },\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"java","label":"Java","source":"import java.net.URI;\nimport java.net.http.HttpClient;\nimport java.net.http.HttpRequest;\nimport java.net.http.HttpResponse;\n\nHttpClient client = HttpClient.newHttpClient();\n\nHttpRequest request = HttpRequest.newBuilder()\n    .uri(URI.create(\"https://api.revkeen.com/v2/customers?limit=20&offset=0\"))\n    .header(\"x-api-key\", System.getenv(\"REVKEEN_API_KEY\"))\n    .GET()\n    .build();\n\nHttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());\nSystem.out.println(response.body());"},{"lang":"ruby","label":"Ruby","source":"require \"net/http\"\nrequire \"json\"\nrequire \"uri\"\n\nuri = URI(\"https://api.revkeen.com/v2/customers?limit=20&offset=0\")\nhttp = Net::HTTP.new(uri.host, uri.port)\nhttp.use_ssl = true\n\nrequest = Net::HTTP::Get.new(uri)\nrequest[\"x-api-key\"] = ENV[\"REVKEEN_API_KEY\"]\n\nresponse = http.request(request)\nputs response.body"}],"security":[{"apiKey":["customers:read"]},{"oauth2":["customers:read"]}]}},"/customers/{id}":{"get":{"operationId":"customers_get","tags":["Customers"],"x-fern-sdk-group-name":["customers"],"summary":"Get customer by ID","description":"Retrieve detailed information about a specific customer\n\n---\n\n**Related endpoints**\n\n- `GET /customers/{id}/state` — Get customer billing state\n- `POST /customers` — Create a new customer\n- `GET /customers` — List customers\n- `PATCH /customers/{id}` — Update customer details\n- `GET /customers/{id}/payment-methods` — Get customer payment methods\n\n**Common errors**\n\n- `401 authentication_error` — missing, invalid, expired, or revoked credential. Codes: `authentication_failed`, `invalid_api_key`, `expired_api_key`, `api_key_revoked`, `session_invalid`, `merchant_required`. Carries a `WWW-Authenticate: Bearer` challenge.\n- `404 resource_missing` — the referenced resource does not exist or is not visible to your key.","parameters":[{"schema":{"type":"string","format":"uuid","description":"Customer UUID"},"required":true,"description":"Customer UUID","name":"id","in":"path"}],"responses":{"200":{"description":"Customer details","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CustomerRetrieveResponse"}}}},"401":{"description":"Unauthorized","headers":{"WWW-Authenticate":{"description":"RFC 6750 Bearer authentication challenge","schema":{"type":"string"},"example":"Bearer realm=\"revkeen-api\""}}},"404":{"description":"Customer not found"},"429":{"description":"Too Many Requests - Rate limit exceeded","headers":{"Retry-After":{"description":"Whole seconds until the caller may retry","schema":{"type":"string"},"example":"60"},"X-RateLimit-Limit":{"description":"Request quota for the current window","schema":{"type":"string"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window","schema":{"type":"string"}},"X-RateLimit-Reset":{"description":"UNIX timestamp in seconds when the window resets","schema":{"type":"string"}}}}},"x-cli-group":"customers","x-cli-name":"get","x-codeSamples":[{"lang":"curl","label":"cURL","source":"curl -X GET \"https://api.revkeen.com/v2/customers/11111111-1111-4111-8111-111111111111\" \\\n  -H \"x-api-key: $REVKEEN_API_KEY\""},{"lang":"typescript","label":"TypeScript (SDK)","source":"// npm install @revkeen/sdk@1.20260822.1541\nimport { RevKeenClient } from \"@revkeen/sdk\";\nconst apiKey = process.env.REVKEEN_API_KEY;\nif (!apiKey) throw new Error(\"Set REVKEEN_API_KEY\");\nconst client = new RevKeenClient({ apiKey, baseUrl: \"https://api.revkeen.com/v2\" });\nconst result = await client.customers.get(\"11111111-1111-4111-8111-111111111111\");\nconsole.log(result);"},{"lang":"go","label":"Go (HTTP)","source":"package main\nimport (\"fmt\"; \"io\"; \"net/http\"; \"os\"; \"strings\"; \"time\")\nfunc main() {\n  req, err := http.NewRequest(\"GET\", \"https://api.revkeen.com/v2/customers/11111111-1111-4111-8111-111111111111\", strings.NewReader(\"\"))\n  if err != nil { panic(err) }\n  req.Header.Set(\"x-api-key\", os.Getenv(\"REVKEEN_API_KEY\"))\n  req.Header.Set(\"Content-Type\", \"application/json\")\n  client := &http.Client{Timeout: 10 * time.Second}\n  response, err := client.Do(req)\n  if err != nil { panic(err) }\n  defer response.Body.Close()\n  if response.StatusCode >= 400 { panic(fmt.Sprintf(\"RevKeen request failed (%d)\", response.StatusCode)) }\n  data, err := io.ReadAll(response.Body)\n  if err != nil { panic(err) }\n  fmt.Println(string(data))\n}"},{"lang":"php","label":"PHP (HTTP)","source":"<?php\n$request = curl_init(\"https://api.revkeen.com/v2/customers/11111111-1111-4111-8111-111111111111\");\ncurl_setopt_array($request, [\n  CURLOPT_CUSTOMREQUEST => \"GET\",\n  CURLOPT_RETURNTRANSFER => true,\n  CURLOPT_TIMEOUT => 10,\n  CURLOPT_HTTPHEADER => [\"x-api-key: \" . getenv(\"REVKEEN_API_KEY\"), \"Content-Type: application/json\"],\n]);\n$response = curl_exec($request);\nif ($response === false) throw new RuntimeException(curl_error($request));\n$status = curl_getinfo($request, CURLINFO_HTTP_CODE);\ncurl_close($request);\nif ($status >= 400) throw new RuntimeException(\"RevKeen request failed ($status)\");\necho $response;"},{"lang":"python","label":"Python","source":"import os\n\nimport requests\n\nresponse = requests.get(\n    \"https://api.revkeen.com/v2/customers/11111111-1111-4111-8111-111111111111\",\n    headers={\"x-api-key\": os.environ[\"REVKEEN_API_KEY\"]},\n)\n\nresponse.raise_for_status()\nprint(response.json())"},{"lang":"javascript","label":"JavaScript","source":"const credential = process.env.REVKEEN_API_KEY;\nif (!credential) throw new Error(\"Set REVKEEN_API_KEY\");\nconst response = await fetch(\"https://api.revkeen.com/v2/customers/11111111-1111-4111-8111-111111111111\", {\n  method: \"GET\",\n  headers: {\n    \"x-api-key\": credential,\n  },\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"java","label":"Java","source":"import java.net.URI;\nimport java.net.http.HttpClient;\nimport java.net.http.HttpRequest;\nimport java.net.http.HttpResponse;\n\nHttpClient client = HttpClient.newHttpClient();\n\nHttpRequest request = HttpRequest.newBuilder()\n    .uri(URI.create(\"https://api.revkeen.com/v2/customers/11111111-1111-4111-8111-111111111111\"))\n    .header(\"x-api-key\", System.getenv(\"REVKEEN_API_KEY\"))\n    .GET()\n    .build();\n\nHttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());\nSystem.out.println(response.body());"},{"lang":"ruby","label":"Ruby","source":"require \"net/http\"\nrequire \"json\"\nrequire \"uri\"\n\nuri = URI(\"https://api.revkeen.com/v2/customers/11111111-1111-4111-8111-111111111111\")\nhttp = Net::HTTP.new(uri.host, uri.port)\nhttp.use_ssl = true\n\nrequest = Net::HTTP::Get.new(uri)\nrequest[\"x-api-key\"] = ENV[\"REVKEEN_API_KEY\"]\n\nresponse = http.request(request)\nputs response.body"}],"security":[{"apiKey":["customers:read"]},{"oauth2":["customers:read"]}]},"patch":{"operationId":"customers_update","tags":["Customers"],"x-fern-sdk-group-name":["customers"],"summary":"Update customer details","description":"Update an existing customer's information\n\n---\n\n**Related endpoints**\n\n- `GET /customers/{id}/state` — Get customer billing state\n- `POST /customers` — Create a new customer\n- `GET /customers` — List customers\n- `GET /customers/{id}` — Get customer by ID\n- `GET /customers/{id}/payment-methods` — Get customer payment methods\n\n**Common errors**\n\n- `400 invalid_request` — malformed payload or failed validation.\n- `401 authentication_error` — missing, invalid, expired, or revoked credential. Codes: `authentication_failed`, `invalid_api_key`, `expired_api_key`, `api_key_revoked`, `session_invalid`, `merchant_required`. Carries a `WWW-Authenticate: Bearer` challenge.\n- `404 resource_missing` — the referenced resource does not exist or is not visible to your key.\n- `409 conflict` — Idempotency-Key collision with a different body, or a concurrent state-transition conflict.\n\n**Idempotency**\n\nPass an `Idempotency-Key` header (UUID v4 recommended) to make retries safe. Keys are valid for 24 hours; see [the idempotency guide](/docs/fundamentals/idempotency).","parameters":[{"schema":{"type":"string","format":"uuid","description":"Customer UUID"},"required":true,"description":"Customer UUID","name":"id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"email":{"type":"string","format":"email","description":"Updated email address"},"external_id":{"type":"string","minLength":1,"maxLength":255,"description":"Your system's identifier for this customer. Unique per merchant and source: no two customers whose external id you set through the API can share one, but a customer synced from an integration can carry the same value. Cannot be changed on a customer that is linked to an integration."},"name":{"type":"string","description":"Updated full name"},"phone":{"type":"string","description":"Updated phone number"},"company_name":{"type":"string","description":"Updated company name"},"address_line1":{"type":"string","description":"Updated billing address line 1"},"address_line2":{"type":"string","description":"Updated billing address line 2"},"city":{"type":"string","description":"Updated billing address city"},"state":{"type":"string","description":"Updated billing address state or county"},"postal_code":{"type":"string","description":"Updated billing address postal code"},"country":{"type":"string","description":"Updated billing address country code"},"metadata":{"type":"object","additionalProperties":true,"description":"Metadata to merge with existing values (set a key to null to remove it)"},"custom_fields":{"type":"object","additionalProperties":true,"description":"Merchant-defined custom fields displayed as columns in the dashboard"}},"additionalProperties":false}}}},"responses":{"200":{"description":"Customer updated successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CustomerUpdateResponse"}}}},"400":{"description":"Validation error, including a change of external_id on a customer that is linked to an integration. `param` names the body field to fix; an unknown field is rejected.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BodyValidationError"}}}},"401":{"description":"Unauthorized","headers":{"WWW-Authenticate":{"description":"RFC 6750 Bearer authentication challenge","schema":{"type":"string"},"example":"Bearer realm=\"revkeen-api\""}}},"404":{"description":"Customer not found"},"409":{"description":"Another customer of this merchant already uses this email address (param email), or another customer whose external id was set through the API already uses this external id (param external_id)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConflictError"}}}},"429":{"description":"Too Many Requests - Rate limit exceeded","headers":{"Retry-After":{"description":"Whole seconds until the caller may retry","schema":{"type":"string"},"example":"60"},"X-RateLimit-Limit":{"description":"Request quota for the current window","schema":{"type":"string"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window","schema":{"type":"string"}},"X-RateLimit-Reset":{"description":"UNIX timestamp in seconds when the window resets","schema":{"type":"string"}}}}},"x-cli-group":"customers","x-cli-name":"update","x-codeSamples":[{"lang":"curl","label":"cURL","source":"curl -X PATCH \"https://api.revkeen.com/v2/customers/11111111-1111-4111-8111-111111111111\" \\\n  -H \"x-api-key: $REVKEEN_API_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"email\": \"customer@example.com\",\n    \"name\": \"example\",\n    \"phone\": \"+15551234567\",\n    \"city\": \"example\"\n  }'"},{"lang":"typescript","label":"TypeScript (SDK)","source":"// npm install @revkeen/sdk@1.20260822.1541\nimport { RevKeenClient } from \"@revkeen/sdk\";\nconst apiKey = process.env.REVKEEN_API_KEY;\nif (!apiKey) throw new Error(\"Set REVKEEN_API_KEY\");\nconst client = new RevKeenClient({ apiKey, baseUrl: \"https://api.revkeen.com/v2\" });\nconst result = await client.customers.update(\"11111111-1111-4111-8111-111111111111\", {\n  \"email\": \"customer@example.com\",\n  \"name\": \"example\",\n  \"phone\": \"+15551234567\",\n  \"city\": \"example\"\n});\nconsole.log(result);"},{"lang":"go","label":"Go (HTTP)","source":"package main\nimport (\"fmt\"; \"io\"; \"net/http\"; \"os\"; \"strings\"; \"time\")\nfunc main() {\n  req, err := http.NewRequest(\"PATCH\", \"https://api.revkeen.com/v2/customers/11111111-1111-4111-8111-111111111111\", strings.NewReader(\"{\\\"email\\\":\\\"customer@example.com\\\",\\\"name\\\":\\\"example\\\",\\\"phone\\\":\\\"+15551234567\\\",\\\"city\\\":\\\"example\\\"}\"))\n  if err != nil { panic(err) }\n  req.Header.Set(\"x-api-key\", os.Getenv(\"REVKEEN_API_KEY\"))\n  req.Header.Set(\"Content-Type\", \"application/json\")\n  client := &http.Client{Timeout: 10 * time.Second}\n  response, err := client.Do(req)\n  if err != nil { panic(err) }\n  defer response.Body.Close()\n  if response.StatusCode >= 400 { panic(fmt.Sprintf(\"RevKeen request failed (%d)\", response.StatusCode)) }\n  data, err := io.ReadAll(response.Body)\n  if err != nil { panic(err) }\n  fmt.Println(string(data))\n}"},{"lang":"php","label":"PHP (HTTP)","source":"<?php\n$request = curl_init(\"https://api.revkeen.com/v2/customers/11111111-1111-4111-8111-111111111111\");\ncurl_setopt_array($request, [\n  CURLOPT_CUSTOMREQUEST => \"PATCH\",\n  CURLOPT_RETURNTRANSFER => true,\n  CURLOPT_TIMEOUT => 10,\n  CURLOPT_HTTPHEADER => [\"x-api-key: \" . getenv(\"REVKEEN_API_KEY\"), \"Content-Type: application/json\"],\n  CURLOPT_POSTFIELDS => '{\\\"email\\\":\\\"customer@example.com\\\",\\\"name\\\":\\\"example\\\",\\\"phone\\\":\\\"+15551234567\\\",\\\"city\\\":\\\"example\\\"}',\n]);\n$response = curl_exec($request);\nif ($response === false) throw new RuntimeException(curl_error($request));\n$status = curl_getinfo($request, CURLINFO_HTTP_CODE);\ncurl_close($request);\nif ($status >= 400) throw new RuntimeException(\"RevKeen request failed ($status)\");\necho $response;"},{"lang":"python","label":"Python","source":"import os\n\nimport requests\n\nresponse = requests.patch(\n    \"https://api.revkeen.com/v2/customers/11111111-1111-4111-8111-111111111111\",\n    headers={\"x-api-key\": os.environ[\"REVKEEN_API_KEY\"]},\n    json={\n        \"email\": \"customer@example.com\",\n        \"name\": \"example\",\n        \"phone\": \"+15551234567\",\n        \"city\": \"example\",\n    },\n)\n\nresponse.raise_for_status()\nprint(response.json())"},{"lang":"javascript","label":"JavaScript","source":"const credential = process.env.REVKEEN_API_KEY;\nif (!credential) throw new Error(\"Set REVKEEN_API_KEY\");\nconst response = await fetch(\"https://api.revkeen.com/v2/customers/11111111-1111-4111-8111-111111111111\", {\n  method: \"PATCH\",\n  headers: {\n    \"x-api-key\": credential,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n    \"email\": \"customer@example.com\",\n    \"name\": \"example\",\n    \"phone\": \"+15551234567\",\n    \"city\": \"example\"\n  }),\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"java","label":"Java","source":"import java.net.URI;\nimport java.net.http.HttpClient;\nimport java.net.http.HttpRequest;\nimport java.net.http.HttpResponse;\n\nHttpClient client = HttpClient.newHttpClient();\n\nHttpRequest request = HttpRequest.newBuilder()\n    .uri(URI.create(\"https://api.revkeen.com/v2/customers/11111111-1111-4111-8111-111111111111\"))\n    .header(\"x-api-key\", System.getenv(\"REVKEEN_API_KEY\"))\n    .header(\"Content-Type\", \"application/json\")\n    .method(\"PATCH\", HttpRequest.BodyPublishers.ofString(\"\"\"\n        {\n          \"email\": \"customer@example.com\",\n          \"name\": \"example\",\n          \"phone\": \"+15551234567\",\n          \"city\": \"example\"\n        }\n        \"\"\"))\n    .build();\n\nHttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());\nSystem.out.println(response.body());"},{"lang":"ruby","label":"Ruby","source":"require \"net/http\"\nrequire \"json\"\nrequire \"uri\"\n\nuri = URI(\"https://api.revkeen.com/v2/customers/11111111-1111-4111-8111-111111111111\")\nhttp = Net::HTTP.new(uri.host, uri.port)\nhttp.use_ssl = true\n\nrequest = Net::HTTP::Patch.new(uri)\nrequest[\"x-api-key\"] = ENV[\"REVKEEN_API_KEY\"]\nrequest[\"Content-Type\"] = \"application/json\"\nrequest.body = {\n  \"email\" => \"customer@example.com\",\n  \"name\" => \"example\",\n  \"phone\" => \"+15551234567\",\n  \"city\" => \"example\",\n}.to_json\n\nresponse = http.request(request)\nputs response.body"}],"security":[{"apiKey":["customers:write"]},{"oauth2":["customers:write"]}]}},"/customers/{id}/payment-methods":{"get":{"operationId":"customers_payment_methods_list","tags":["Customers","Payment Methods"],"x-fern-sdk-group-name":["customers","paymentMethods"],"summary":"Get customer payment methods","description":"Retrieve all payment methods for a customer\n\n---\n\n**Related endpoints**\n\n- `GET /customers/{id}/state` — Get customer billing state\n- `POST /customers` — Create a new customer\n- `GET /customers` — List customers\n- `GET /customers/{id}` — Get customer by ID\n- `PATCH /customers/{id}` — Update customer details\n\n**Common errors**\n\n- `401 authentication_error` — missing, invalid, expired, or revoked credential. Codes: `authentication_failed`, `invalid_api_key`, `expired_api_key`, `api_key_revoked`, `session_invalid`, `merchant_required`. Carries a `WWW-Authenticate: Bearer` challenge.\n- `404 resource_missing` — the referenced resource does not exist or is not visible to your key.\n\n**Pagination**\n\nOffset-based with `limit` (default 25, max 100) and `offset`. The response `pagination` block includes `total` and `hasMore`. See [the pagination guide](/docs/fundamentals/pagination) for SDK auto-paging helpers.","parameters":[{"schema":{"type":"string","format":"uuid","description":"Customer UUID"},"required":true,"description":"Customer UUID","name":"id","in":"path"}],"responses":{"200":{"description":"Payment methods list","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CustomerPaymentMethodsListResponse"}}}},"401":{"description":"Unauthorized","headers":{"WWW-Authenticate":{"description":"RFC 6750 Bearer authentication challenge","schema":{"type":"string"},"example":"Bearer realm=\"revkeen-api\""}}},"404":{"description":"Customer not found"},"429":{"description":"Too Many Requests - Rate limit exceeded","headers":{"Retry-After":{"description":"Whole seconds until the caller may retry","schema":{"type":"string"},"example":"60"},"X-RateLimit-Limit":{"description":"Request quota for the current window","schema":{"type":"string"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window","schema":{"type":"string"}},"X-RateLimit-Reset":{"description":"UNIX timestamp in seconds when the window resets","schema":{"type":"string"}}}}},"x-cli-group":"customers","x-cli-name":"payment-methods-list","x-codeSamples":[{"lang":"curl","label":"cURL","source":"curl -X GET \"https://api.revkeen.com/v2/customers/11111111-1111-4111-8111-111111111111/payment-methods\" \\\n  -H \"x-api-key: $REVKEEN_API_KEY\""},{"lang":"typescript","label":"TypeScript (SDK)","source":"// npm install @revkeen/sdk@1.20260822.1541\nimport { RevKeenClient } from \"@revkeen/sdk\";\nconst apiKey = process.env.REVKEEN_API_KEY;\nif (!apiKey) throw new Error(\"Set REVKEEN_API_KEY\");\nconst client = new RevKeenClient({ apiKey, baseUrl: \"https://api.revkeen.com/v2\" });\nconst result = await client.customers.paymentMethodsList(\"11111111-1111-4111-8111-111111111111\");\nconsole.log(result);"},{"lang":"go","label":"Go (HTTP)","source":"package main\nimport (\"fmt\"; \"io\"; \"net/http\"; \"os\"; \"strings\"; \"time\")\nfunc main() {\n  req, err := http.NewRequest(\"GET\", \"https://api.revkeen.com/v2/customers/11111111-1111-4111-8111-111111111111/payment-methods\", strings.NewReader(\"\"))\n  if err != nil { panic(err) }\n  req.Header.Set(\"x-api-key\", os.Getenv(\"REVKEEN_API_KEY\"))\n  req.Header.Set(\"Content-Type\", \"application/json\")\n  client := &http.Client{Timeout: 10 * time.Second}\n  response, err := client.Do(req)\n  if err != nil { panic(err) }\n  defer response.Body.Close()\n  if response.StatusCode >= 400 { panic(fmt.Sprintf(\"RevKeen request failed (%d)\", response.StatusCode)) }\n  data, err := io.ReadAll(response.Body)\n  if err != nil { panic(err) }\n  fmt.Println(string(data))\n}"},{"lang":"php","label":"PHP (HTTP)","source":"<?php\n$request = curl_init(\"https://api.revkeen.com/v2/customers/11111111-1111-4111-8111-111111111111/payment-methods\");\ncurl_setopt_array($request, [\n  CURLOPT_CUSTOMREQUEST => \"GET\",\n  CURLOPT_RETURNTRANSFER => true,\n  CURLOPT_TIMEOUT => 10,\n  CURLOPT_HTTPHEADER => [\"x-api-key: \" . getenv(\"REVKEEN_API_KEY\"), \"Content-Type: application/json\"],\n]);\n$response = curl_exec($request);\nif ($response === false) throw new RuntimeException(curl_error($request));\n$status = curl_getinfo($request, CURLINFO_HTTP_CODE);\ncurl_close($request);\nif ($status >= 400) throw new RuntimeException(\"RevKeen request failed ($status)\");\necho $response;"},{"lang":"python","label":"Python","source":"import os\n\nimport requests\n\nresponse = requests.get(\n    \"https://api.revkeen.com/v2/customers/11111111-1111-4111-8111-111111111111/payment-methods\",\n    headers={\"x-api-key\": os.environ[\"REVKEEN_API_KEY\"]},\n)\n\nresponse.raise_for_status()\nprint(response.json())"},{"lang":"javascript","label":"JavaScript","source":"const credential = process.env.REVKEEN_API_KEY;\nif (!credential) throw new Error(\"Set REVKEEN_API_KEY\");\nconst response = await fetch(\"https://api.revkeen.com/v2/customers/11111111-1111-4111-8111-111111111111/payment-methods\", {\n  method: \"GET\",\n  headers: {\n    \"x-api-key\": credential,\n  },\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"java","label":"Java","source":"import java.net.URI;\nimport java.net.http.HttpClient;\nimport java.net.http.HttpRequest;\nimport java.net.http.HttpResponse;\n\nHttpClient client = HttpClient.newHttpClient();\n\nHttpRequest request = HttpRequest.newBuilder()\n    .uri(URI.create(\"https://api.revkeen.com/v2/customers/11111111-1111-4111-8111-111111111111/payment-methods\"))\n    .header(\"x-api-key\", System.getenv(\"REVKEEN_API_KEY\"))\n    .GET()\n    .build();\n\nHttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());\nSystem.out.println(response.body());"},{"lang":"ruby","label":"Ruby","source":"require \"net/http\"\nrequire \"json\"\nrequire \"uri\"\n\nuri = URI(\"https://api.revkeen.com/v2/customers/11111111-1111-4111-8111-111111111111/payment-methods\")\nhttp = Net::HTTP.new(uri.host, uri.port)\nhttp.use_ssl = true\n\nrequest = Net::HTTP::Get.new(uri)\nrequest[\"x-api-key\"] = ENV[\"REVKEEN_API_KEY\"]\n\nresponse = http.request(request)\nputs response.body"}],"security":[{"apiKey":["customers:read"]},{"oauth2":["customers:read"]}]}},"/subscriptions":{"post":{"operationId":"subscriptions_create","tags":["Subscriptions"],"x-fern-sdk-group-name":["subscriptions"],"summary":"Create a new subscription","description":"Create a new subscription for a customer.\n\n**The first period is billed at creation**, at the price's amount for the quantity. On card the saved `paymentMethodId` is charged straight away (it is required unless the subscription starts in a trial). On Direct Debit the first collection is scheduled with advance notice, typically 5–7 working days after creation. With a trial, nothing is billed until the trial ends. A trial starts from `trialEnd`, from `trial_period_days`, or, when the request names neither, from the price's own `trial_period_days`; send `trial_period_days: 0` to start without the price's trial. `trialEnd` and `trial_period_days` cannot be combined (400 `trial_conflict`).\n\nSet `paymentRail: direct_debit` with an active `mandateId` to create a Direct Debit subscription (GBP only). This needs a signed-in team member or an OAuth app; API keys are refused with 403.\n\n---\n\n**Related endpoints**\n\n- `GET /subscriptions` — List subscriptions\n- `GET /subscriptions/{id}` — Get subscription by ID\n- `PATCH /subscriptions/{id}` — Update subscription details\n- `POST /subscriptions/{id}/change-plan` — Change subscription plan\n- `POST /subscriptions/{id}/preview-change-plan` — Preview a plan change\n- `POST /subscriptions/{id}/pause` — Pause subscription\n- `POST /subscriptions/{id}/resume` — Resume subscription\n- `POST /subscriptions/{id}/cancel` — Cancel subscription\n\n**Common errors**\n\n- `400 invalid_request` — malformed payload or failed validation.\n- `401 authentication_error` — missing, invalid, expired, or revoked credential. Codes: `authentication_failed`, `invalid_api_key`, `expired_api_key`, `api_key_revoked`, `session_invalid`, `merchant_required`. Carries a `WWW-Authenticate: Bearer` challenge.\n- `403 authorization_error` — the caller is identified but the action is denied. Codes include `insufficient_permissions`, `ip_not_allowed`, `merchant_mismatch`, and `origin_not_allowed`.\n- `404 resource_missing` — the referenced resource does not exist or is not visible to your key.\n- `422 unprocessable_entity` — business-rule failure (for example, refunding more than the original charge).\n\n**Idempotency**\n\nPass an `Idempotency-Key` header (UUID v4 recommended) to make retries safe. Keys are valid for 24 hours; see [the idempotency guide](/docs/fundamentals/idempotency).","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"customerId":{"type":"string","format":"uuid"},"productId":{"type":"string","format":"uuid"},"priceId":{"type":"string","format":"uuid"},"paymentMethodId":{"type":"string","format":"uuid"},"startDate":{"type":"string"},"currency":{"type":"string","description":"Defaults to the price's currency"},"quantity":{"type":"integer","minimum":1,"default":1},"trialEnd":{"type":"string","format":"date-time"},"trial_period_days":{"type":"integer","minimum":0,"maximum":730,"description":"Days of free trial, counted from the start of the first period. Overrides the price's own `trial_period_days`; `0` starts the subscription without the price's trial. Cannot be combined with `trialEnd`."},"billingStartsOn":{"type":"string","format":"date-time"},"metadata":{"type":"object","additionalProperties":{}},"paymentRail":{"type":"string","enum":["card","direct_debit"],"description":"How the subscription is collected. `direct_debit` needs `mandateId` and a GBP price, and cannot be combined with `paymentMethodId`. It is available to signed-in team members and OAuth apps; API keys are refused with 403."},"mandateId":{"type":"string","format":"uuid","description":"An active Direct Debit mandate belonging to this customer. Only with `paymentRail: direct_debit`."}},"required":["customerId"],"additionalProperties":false}}}},"responses":{"201":{"description":"Subscription created successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubscriptionCreateResponse"}}}},"400":{"description":"Validation error"},"401":{"description":"Unauthorized","headers":{"WWW-Authenticate":{"description":"RFC 6750 Bearer authentication challenge","schema":{"type":"string"},"example":"Bearer realm=\"revkeen-api\""}}},"403":{"description":"Not allowed. A Direct Debit subscription needs a signed-in team member or OAuth app with access to mandates; API keys are refused. Also returned when Direct Debit is not enabled."},"404":{"description":"The mandate, payment method, product or price was not found for this customer"},"422":{"description":"The Direct Debit mandate cannot collect yet, or Direct Debit is not configured"},"429":{"description":"Too Many Requests - Rate limit exceeded","headers":{"Retry-After":{"description":"Whole seconds until the caller may retry","schema":{"type":"string"},"example":"60"},"X-RateLimit-Limit":{"description":"Request quota for the current window","schema":{"type":"string"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window","schema":{"type":"string"}},"X-RateLimit-Reset":{"description":"UNIX timestamp in seconds when the window resets","schema":{"type":"string"}}}}},"x-cli-group":"subscriptions","x-cli-name":"create","x-codeSamples":[{"lang":"curl","label":"cURL","source":"curl -X POST \"https://api.revkeen.com/v2/subscriptions\" \\\n  -H \"x-api-key: $REVKEEN_API_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"customerId\": \"11111111-1111-4111-8111-111111111111\"\n  }'"},{"lang":"typescript","label":"TypeScript (SDK)","source":"// npm install @revkeen/sdk@1.20260822.1541\nimport { RevKeenClient } from \"@revkeen/sdk\";\nconst apiKey = process.env.REVKEEN_API_KEY;\nif (!apiKey) throw new Error(\"Set REVKEEN_API_KEY\");\nconst client = new RevKeenClient({ apiKey, baseUrl: \"https://api.revkeen.com/v2\" });\nconst result = await client.subscriptions.create({\n  \"customerId\": \"11111111-1111-4111-8111-111111111111\"\n});\nconsole.log(result);"},{"lang":"go","label":"Go (HTTP)","source":"package main\nimport (\"fmt\"; \"io\"; \"net/http\"; \"os\"; \"strings\"; \"time\")\nfunc main() {\n  req, err := http.NewRequest(\"POST\", \"https://api.revkeen.com/v2/subscriptions\", strings.NewReader(\"{\\\"customerId\\\":\\\"11111111-1111-4111-8111-111111111111\\\"}\"))\n  if err != nil { panic(err) }\n  req.Header.Set(\"x-api-key\", os.Getenv(\"REVKEEN_API_KEY\"))\n  req.Header.Set(\"Content-Type\", \"application/json\")\n  client := &http.Client{Timeout: 10 * time.Second}\n  response, err := client.Do(req)\n  if err != nil { panic(err) }\n  defer response.Body.Close()\n  if response.StatusCode >= 400 { panic(fmt.Sprintf(\"RevKeen request failed (%d)\", response.StatusCode)) }\n  data, err := io.ReadAll(response.Body)\n  if err != nil { panic(err) }\n  fmt.Println(string(data))\n}"},{"lang":"php","label":"PHP (HTTP)","source":"<?php\n$request = curl_init(\"https://api.revkeen.com/v2/subscriptions\");\ncurl_setopt_array($request, [\n  CURLOPT_CUSTOMREQUEST => \"POST\",\n  CURLOPT_RETURNTRANSFER => true,\n  CURLOPT_TIMEOUT => 10,\n  CURLOPT_HTTPHEADER => [\"x-api-key: \" . getenv(\"REVKEEN_API_KEY\"), \"Content-Type: application/json\"],\n  CURLOPT_POSTFIELDS => '{\\\"customerId\\\":\\\"11111111-1111-4111-8111-111111111111\\\"}',\n]);\n$response = curl_exec($request);\nif ($response === false) throw new RuntimeException(curl_error($request));\n$status = curl_getinfo($request, CURLINFO_HTTP_CODE);\ncurl_close($request);\nif ($status >= 400) throw new RuntimeException(\"RevKeen request failed ($status)\");\necho $response;"},{"lang":"python","label":"Python","source":"import os\n\nimport requests\n\nresponse = requests.post(\n    \"https://api.revkeen.com/v2/subscriptions\",\n    headers={\"x-api-key\": os.environ[\"REVKEEN_API_KEY\"]},\n    json={\n        \"customerId\": \"11111111-1111-4111-8111-111111111111\",\n    },\n)\n\nresponse.raise_for_status()\nprint(response.json())"},{"lang":"javascript","label":"JavaScript","source":"const credential = process.env.REVKEEN_API_KEY;\nif (!credential) throw new Error(\"Set REVKEEN_API_KEY\");\nconst response = await fetch(\"https://api.revkeen.com/v2/subscriptions\", {\n  method: \"POST\",\n  headers: {\n    \"x-api-key\": credential,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n    \"customerId\": \"11111111-1111-4111-8111-111111111111\"\n  }),\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"java","label":"Java","source":"import java.net.URI;\nimport java.net.http.HttpClient;\nimport java.net.http.HttpRequest;\nimport java.net.http.HttpResponse;\n\nHttpClient client = HttpClient.newHttpClient();\n\nHttpRequest request = HttpRequest.newBuilder()\n    .uri(URI.create(\"https://api.revkeen.com/v2/subscriptions\"))\n    .header(\"x-api-key\", System.getenv(\"REVKEEN_API_KEY\"))\n    .header(\"Content-Type\", \"application/json\")\n    .method(\"POST\", HttpRequest.BodyPublishers.ofString(\"\"\"\n        {\n          \"customerId\": \"11111111-1111-4111-8111-111111111111\"\n        }\n        \"\"\"))\n    .build();\n\nHttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());\nSystem.out.println(response.body());"},{"lang":"ruby","label":"Ruby","source":"require \"net/http\"\nrequire \"json\"\nrequire \"uri\"\n\nuri = URI(\"https://api.revkeen.com/v2/subscriptions\")\nhttp = Net::HTTP.new(uri.host, uri.port)\nhttp.use_ssl = true\n\nrequest = Net::HTTP::Post.new(uri)\nrequest[\"x-api-key\"] = ENV[\"REVKEEN_API_KEY\"]\nrequest[\"Content-Type\"] = \"application/json\"\nrequest.body = {\n  \"customerId\" => \"11111111-1111-4111-8111-111111111111\",\n}.to_json\n\nresponse = http.request(request)\nputs response.body"}],"security":[{"apiKey":["subscriptions:write"]},{"oauth2":["subscriptions:write"]}]},"get":{"operationId":"subscriptions_list","tags":["Subscriptions"],"x-fern-sdk-group-name":["subscriptions"],"summary":"List subscriptions","description":"Retrieve a paginated list of subscriptions with optional filters.\n\nEvery query parameter is optional: a bare call lists the merchant's subscriptions, newest first, with a default page size of 20. `customerId` must name a customer of the authenticated merchant; an unknown id is rejected with 404 `resource_missing` rather than returning an empty list, so a mistyped filter is not mistaken for a customer who has no subscriptions.\n\n---\n\n**Related endpoints**\n\n- `POST /subscriptions` — Create a new subscription\n- `GET /subscriptions/{id}` — Get subscription by ID\n- `PATCH /subscriptions/{id}` — Update subscription details\n- `POST /subscriptions/{id}/change-plan` — Change subscription plan\n- `POST /subscriptions/{id}/preview-change-plan` — Preview a plan change\n- `POST /subscriptions/{id}/pause` — Pause subscription\n- `POST /subscriptions/{id}/resume` — Resume subscription\n- `POST /subscriptions/{id}/cancel` — Cancel subscription\n\n**Common errors**\n\n- `401 authentication_error` — missing, invalid, expired, or revoked credential. Codes: `authentication_failed`, `invalid_api_key`, `expired_api_key`, `api_key_revoked`, `session_invalid`, `merchant_required`. Carries a `WWW-Authenticate: Bearer` challenge.\n- `404 resource_missing` — the referenced resource does not exist or is not visible to your key.\n\n**Pagination**\n\nOffset-based with `limit` (default 25, max 100) and `offset`. The response `pagination` block includes `total` and `hasMore`. See [the pagination guide](/docs/fundamentals/pagination) for SDK auto-paging helpers.","parameters":[{"schema":{"type":"integer","minimum":1,"default":1,"description":"Page number"},"required":false,"description":"Page number","name":"page","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":100,"default":20,"description":"Maximum number of results (1-100)"},"required":false,"description":"Maximum number of results (1-100)","name":"limit","in":"query"},{"schema":{"type":"string","enum":["active","canceled","past_due","trialing","paused","incomplete","incomplete_expired"],"description":"Filter by subscription status"},"required":false,"description":"Filter by subscription status","name":"status","in":"query"},{"schema":{"type":"string","format":"uuid","description":"Filter by customer UUID"},"required":false,"description":"Filter by customer UUID","name":"customerId","in":"query"}],"responses":{"200":{"description":"List of subscriptions","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubscriptionListResponse"}}}},"401":{"description":"Unauthorized","headers":{"WWW-Authenticate":{"description":"RFC 6750 Bearer authentication challenge","schema":{"type":"string"},"example":"Bearer realm=\"revkeen-api\""}}},"404":{"description":"`customerId` named no customer of this merchant","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubscriptionCustomerNotFound"}}}},"429":{"description":"Too Many Requests - Rate limit exceeded","headers":{"Retry-After":{"description":"Whole seconds until the caller may retry","schema":{"type":"string"},"example":"60"},"X-RateLimit-Limit":{"description":"Request quota for the current window","schema":{"type":"string"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window","schema":{"type":"string"}},"X-RateLimit-Reset":{"description":"UNIX timestamp in seconds when the window resets","schema":{"type":"string"}}}}},"x-cli-group":"subscriptions","x-cli-name":"list","x-codeSamples":[{"lang":"curl","label":"cURL","source":"curl -X GET \"https://api.revkeen.com/v2/subscriptions?page=1&limit=20\" \\\n  -H \"x-api-key: $REVKEEN_API_KEY\""},{"lang":"typescript","label":"TypeScript (SDK)","source":"// npm install @revkeen/sdk@1.20260822.1541\nimport { RevKeenClient } from \"@revkeen/sdk\";\nconst apiKey = process.env.REVKEEN_API_KEY;\nif (!apiKey) throw new Error(\"Set REVKEEN_API_KEY\");\nconst client = new RevKeenClient({ apiKey, baseUrl: \"https://api.revkeen.com/v2\" });\nconst result = await client.subscriptions.list({\n  \"page\": 1,\n  \"limit\": 20\n});\nconsole.log(result);"},{"lang":"go","label":"Go (HTTP)","source":"package main\nimport (\"fmt\"; \"io\"; \"net/http\"; \"os\"; \"strings\"; \"time\")\nfunc main() {\n  req, err := http.NewRequest(\"GET\", \"https://api.revkeen.com/v2/subscriptions?page=1&limit=20\", strings.NewReader(\"\"))\n  if err != nil { panic(err) }\n  req.Header.Set(\"x-api-key\", os.Getenv(\"REVKEEN_API_KEY\"))\n  req.Header.Set(\"Content-Type\", \"application/json\")\n  client := &http.Client{Timeout: 10 * time.Second}\n  response, err := client.Do(req)\n  if err != nil { panic(err) }\n  defer response.Body.Close()\n  if response.StatusCode >= 400 { panic(fmt.Sprintf(\"RevKeen request failed (%d)\", response.StatusCode)) }\n  data, err := io.ReadAll(response.Body)\n  if err != nil { panic(err) }\n  fmt.Println(string(data))\n}"},{"lang":"php","label":"PHP (HTTP)","source":"<?php\n$request = curl_init(\"https://api.revkeen.com/v2/subscriptions?page=1&limit=20\");\ncurl_setopt_array($request, [\n  CURLOPT_CUSTOMREQUEST => \"GET\",\n  CURLOPT_RETURNTRANSFER => true,\n  CURLOPT_TIMEOUT => 10,\n  CURLOPT_HTTPHEADER => [\"x-api-key: \" . getenv(\"REVKEEN_API_KEY\"), \"Content-Type: application/json\"],\n]);\n$response = curl_exec($request);\nif ($response === false) throw new RuntimeException(curl_error($request));\n$status = curl_getinfo($request, CURLINFO_HTTP_CODE);\ncurl_close($request);\nif ($status >= 400) throw new RuntimeException(\"RevKeen request failed ($status)\");\necho $response;"},{"lang":"python","label":"Python","source":"import os\n\nimport requests\n\nresponse = requests.get(\n    \"https://api.revkeen.com/v2/subscriptions\",\n    headers={\"x-api-key\": os.environ[\"REVKEEN_API_KEY\"]},\n    params={\n        \"page\": 1,\n        \"limit\": 20,\n    },\n)\n\nresponse.raise_for_status()\nprint(response.json())"},{"lang":"javascript","label":"JavaScript","source":"const credential = process.env.REVKEEN_API_KEY;\nif (!credential) throw new Error(\"Set REVKEEN_API_KEY\");\nconst response = await fetch(\"https://api.revkeen.com/v2/subscriptions?page=1&limit=20\", {\n  method: \"GET\",\n  headers: {\n    \"x-api-key\": credential,\n  },\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"java","label":"Java","source":"import java.net.URI;\nimport java.net.http.HttpClient;\nimport java.net.http.HttpRequest;\nimport java.net.http.HttpResponse;\n\nHttpClient client = HttpClient.newHttpClient();\n\nHttpRequest request = HttpRequest.newBuilder()\n    .uri(URI.create(\"https://api.revkeen.com/v2/subscriptions?page=1&limit=20\"))\n    .header(\"x-api-key\", System.getenv(\"REVKEEN_API_KEY\"))\n    .GET()\n    .build();\n\nHttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());\nSystem.out.println(response.body());"},{"lang":"ruby","label":"Ruby","source":"require \"net/http\"\nrequire \"json\"\nrequire \"uri\"\n\nuri = URI(\"https://api.revkeen.com/v2/subscriptions?page=1&limit=20\")\nhttp = Net::HTTP.new(uri.host, uri.port)\nhttp.use_ssl = true\n\nrequest = Net::HTTP::Get.new(uri)\nrequest[\"x-api-key\"] = ENV[\"REVKEEN_API_KEY\"]\n\nresponse = http.request(request)\nputs response.body"}],"security":[{"apiKey":["subscriptions:read"]},{"oauth2":["subscriptions:read"]}]}},"/subscriptions/{id}":{"get":{"operationId":"subscriptions_get","tags":["Subscriptions"],"x-fern-sdk-group-name":["subscriptions"],"summary":"Get subscription by ID","description":"Retrieve detailed information about a specific subscription\n\n---\n\n**Related endpoints**\n\n- `POST /subscriptions` — Create a new subscription\n- `GET /subscriptions` — List subscriptions\n- `PATCH /subscriptions/{id}` — Update subscription details\n- `POST /subscriptions/{id}/change-plan` — Change subscription plan\n- `POST /subscriptions/{id}/preview-change-plan` — Preview a plan change\n- `POST /subscriptions/{id}/pause` — Pause subscription\n- `POST /subscriptions/{id}/resume` — Resume subscription\n- `POST /subscriptions/{id}/cancel` — Cancel subscription\n\n**Common errors**\n\n- `401 authentication_error` — missing, invalid, expired, or revoked credential. Codes: `authentication_failed`, `invalid_api_key`, `expired_api_key`, `api_key_revoked`, `session_invalid`, `merchant_required`. Carries a `WWW-Authenticate: Bearer` challenge.\n- `404 resource_missing` — the referenced resource does not exist or is not visible to your key.","parameters":[{"schema":{"type":"string","format":"uuid","description":"Subscription UUID"},"required":true,"description":"Subscription UUID","name":"id","in":"path"}],"responses":{"200":{"description":"Subscription details","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubscriptionRetrieveResponse"}}}},"401":{"description":"Unauthorized","headers":{"WWW-Authenticate":{"description":"RFC 6750 Bearer authentication challenge","schema":{"type":"string"},"example":"Bearer realm=\"revkeen-api\""}}},"404":{"description":"Subscription not found"},"429":{"description":"Too Many Requests - Rate limit exceeded","headers":{"Retry-After":{"description":"Whole seconds until the caller may retry","schema":{"type":"string"},"example":"60"},"X-RateLimit-Limit":{"description":"Request quota for the current window","schema":{"type":"string"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window","schema":{"type":"string"}},"X-RateLimit-Reset":{"description":"UNIX timestamp in seconds when the window resets","schema":{"type":"string"}}}}},"x-cli-group":"subscriptions","x-cli-name":"get","x-codeSamples":[{"lang":"curl","label":"cURL","source":"curl -X GET \"https://api.revkeen.com/v2/subscriptions/11111111-1111-4111-8111-111111111111\" \\\n  -H \"x-api-key: $REVKEEN_API_KEY\""},{"lang":"typescript","label":"TypeScript (SDK)","source":"// npm install @revkeen/sdk@1.20260822.1541\nimport { RevKeenClient } from \"@revkeen/sdk\";\nconst apiKey = process.env.REVKEEN_API_KEY;\nif (!apiKey) throw new Error(\"Set REVKEEN_API_KEY\");\nconst client = new RevKeenClient({ apiKey, baseUrl: \"https://api.revkeen.com/v2\" });\nconst result = await client.subscriptions.get(\"11111111-1111-4111-8111-111111111111\");\nconsole.log(result);"},{"lang":"go","label":"Go (HTTP)","source":"package main\nimport (\"fmt\"; \"io\"; \"net/http\"; \"os\"; \"strings\"; \"time\")\nfunc main() {\n  req, err := http.NewRequest(\"GET\", \"https://api.revkeen.com/v2/subscriptions/11111111-1111-4111-8111-111111111111\", strings.NewReader(\"\"))\n  if err != nil { panic(err) }\n  req.Header.Set(\"x-api-key\", os.Getenv(\"REVKEEN_API_KEY\"))\n  req.Header.Set(\"Content-Type\", \"application/json\")\n  client := &http.Client{Timeout: 10 * time.Second}\n  response, err := client.Do(req)\n  if err != nil { panic(err) }\n  defer response.Body.Close()\n  if response.StatusCode >= 400 { panic(fmt.Sprintf(\"RevKeen request failed (%d)\", response.StatusCode)) }\n  data, err := io.ReadAll(response.Body)\n  if err != nil { panic(err) }\n  fmt.Println(string(data))\n}"},{"lang":"php","label":"PHP (HTTP)","source":"<?php\n$request = curl_init(\"https://api.revkeen.com/v2/subscriptions/11111111-1111-4111-8111-111111111111\");\ncurl_setopt_array($request, [\n  CURLOPT_CUSTOMREQUEST => \"GET\",\n  CURLOPT_RETURNTRANSFER => true,\n  CURLOPT_TIMEOUT => 10,\n  CURLOPT_HTTPHEADER => [\"x-api-key: \" . getenv(\"REVKEEN_API_KEY\"), \"Content-Type: application/json\"],\n]);\n$response = curl_exec($request);\nif ($response === false) throw new RuntimeException(curl_error($request));\n$status = curl_getinfo($request, CURLINFO_HTTP_CODE);\ncurl_close($request);\nif ($status >= 400) throw new RuntimeException(\"RevKeen request failed ($status)\");\necho $response;"},{"lang":"python","label":"Python","source":"import os\n\nimport requests\n\nresponse = requests.get(\n    \"https://api.revkeen.com/v2/subscriptions/11111111-1111-4111-8111-111111111111\",\n    headers={\"x-api-key\": os.environ[\"REVKEEN_API_KEY\"]},\n)\n\nresponse.raise_for_status()\nprint(response.json())"},{"lang":"javascript","label":"JavaScript","source":"const credential = process.env.REVKEEN_API_KEY;\nif (!credential) throw new Error(\"Set REVKEEN_API_KEY\");\nconst response = await fetch(\"https://api.revkeen.com/v2/subscriptions/11111111-1111-4111-8111-111111111111\", {\n  method: \"GET\",\n  headers: {\n    \"x-api-key\": credential,\n  },\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"java","label":"Java","source":"import java.net.URI;\nimport java.net.http.HttpClient;\nimport java.net.http.HttpRequest;\nimport java.net.http.HttpResponse;\n\nHttpClient client = HttpClient.newHttpClient();\n\nHttpRequest request = HttpRequest.newBuilder()\n    .uri(URI.create(\"https://api.revkeen.com/v2/subscriptions/11111111-1111-4111-8111-111111111111\"))\n    .header(\"x-api-key\", System.getenv(\"REVKEEN_API_KEY\"))\n    .GET()\n    .build();\n\nHttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());\nSystem.out.println(response.body());"},{"lang":"ruby","label":"Ruby","source":"require \"net/http\"\nrequire \"json\"\nrequire \"uri\"\n\nuri = URI(\"https://api.revkeen.com/v2/subscriptions/11111111-1111-4111-8111-111111111111\")\nhttp = Net::HTTP.new(uri.host, uri.port)\nhttp.use_ssl = true\n\nrequest = Net::HTTP::Get.new(uri)\nrequest[\"x-api-key\"] = ENV[\"REVKEEN_API_KEY\"]\n\nresponse = http.request(request)\nputs response.body"}],"security":[{"apiKey":["subscriptions:read"]},{"oauth2":["subscriptions:read"]}]},"patch":{"operationId":"subscriptions_update","tags":["Subscriptions"],"x-fern-sdk-group-name":["subscriptions"],"summary":"Update subscription details","description":"Update a subscription's metadata.\n\nOnly `metadata` is accepted; any other field is refused with 400. To cancel a subscription use `POST /subscriptions/{id}/cancel`.\n\n---\n\n**Related endpoints**\n\n- `POST /subscriptions` — Create a new subscription\n- `GET /subscriptions` — List subscriptions\n- `GET /subscriptions/{id}` — Get subscription by ID\n- `POST /subscriptions/{id}/change-plan` — Change subscription plan\n- `POST /subscriptions/{id}/preview-change-plan` — Preview a plan change\n- `POST /subscriptions/{id}/pause` — Pause subscription\n- `POST /subscriptions/{id}/resume` — Resume subscription\n- `POST /subscriptions/{id}/cancel` — Cancel subscription\n\n**Common errors**\n\n- `400 invalid_request` — malformed payload or failed validation.\n- `401 authentication_error` — missing, invalid, expired, or revoked credential. Codes: `authentication_failed`, `invalid_api_key`, `expired_api_key`, `api_key_revoked`, `session_invalid`, `merchant_required`. Carries a `WWW-Authenticate: Bearer` challenge.\n- `404 resource_missing` — the referenced resource does not exist or is not visible to your key.\n\n**Idempotency**\n\nPass an `Idempotency-Key` header (UUID v4 recommended) to make retries safe. Keys are valid for 24 hours; see [the idempotency guide](/docs/fundamentals/idempotency).","parameters":[{"schema":{"type":"string","format":"uuid","description":"Subscription UUID"},"required":true,"description":"Subscription UUID","name":"id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubscriptionUpdateInput"}}}},"responses":{"200":{"description":"Subscription updated successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubscriptionUpdateResponse"}}}},"400":{"description":"Validation error. Only `metadata` can be updated"},"401":{"description":"Unauthorized","headers":{"WWW-Authenticate":{"description":"RFC 6750 Bearer authentication challenge","schema":{"type":"string"},"example":"Bearer realm=\"revkeen-api\""}}},"404":{"description":"Subscription not found"},"429":{"description":"Too Many Requests - Rate limit exceeded","headers":{"Retry-After":{"description":"Whole seconds until the caller may retry","schema":{"type":"string"},"example":"60"},"X-RateLimit-Limit":{"description":"Request quota for the current window","schema":{"type":"string"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window","schema":{"type":"string"}},"X-RateLimit-Reset":{"description":"UNIX timestamp in seconds when the window resets","schema":{"type":"string"}}}}},"x-cli-group":"subscriptions","x-cli-name":"update","x-codeSamples":[{"lang":"curl","label":"cURL","source":"curl -X PATCH \"https://api.revkeen.com/v2/subscriptions/11111111-1111-4111-8111-111111111111\" \\\n  -H \"x-api-key: $REVKEEN_API_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"metadata\": {\n      \"key\": \"example\"\n    }\n  }'"},{"lang":"typescript","label":"TypeScript (SDK)","source":"// npm install @revkeen/sdk@1.20260822.1541\nimport { RevKeenClient } from \"@revkeen/sdk\";\nconst apiKey = process.env.REVKEEN_API_KEY;\nif (!apiKey) throw new Error(\"Set REVKEEN_API_KEY\");\nconst client = new RevKeenClient({ apiKey, baseUrl: \"https://api.revkeen.com/v2\" });\nconst result = await client.subscriptions.update(\"11111111-1111-4111-8111-111111111111\", {\n  \"metadata\": {\n    \"key\": \"example\"\n  }\n});\nconsole.log(result);"},{"lang":"go","label":"Go (HTTP)","source":"package main\nimport (\"fmt\"; \"io\"; \"net/http\"; \"os\"; \"strings\"; \"time\")\nfunc main() {\n  req, err := http.NewRequest(\"PATCH\", \"https://api.revkeen.com/v2/subscriptions/11111111-1111-4111-8111-111111111111\", strings.NewReader(\"{\\\"metadata\\\":{\\\"key\\\":\\\"example\\\"}}\"))\n  if err != nil { panic(err) }\n  req.Header.Set(\"x-api-key\", os.Getenv(\"REVKEEN_API_KEY\"))\n  req.Header.Set(\"Content-Type\", \"application/json\")\n  client := &http.Client{Timeout: 10 * time.Second}\n  response, err := client.Do(req)\n  if err != nil { panic(err) }\n  defer response.Body.Close()\n  if response.StatusCode >= 400 { panic(fmt.Sprintf(\"RevKeen request failed (%d)\", response.StatusCode)) }\n  data, err := io.ReadAll(response.Body)\n  if err != nil { panic(err) }\n  fmt.Println(string(data))\n}"},{"lang":"php","label":"PHP (HTTP)","source":"<?php\n$request = curl_init(\"https://api.revkeen.com/v2/subscriptions/11111111-1111-4111-8111-111111111111\");\ncurl_setopt_array($request, [\n  CURLOPT_CUSTOMREQUEST => \"PATCH\",\n  CURLOPT_RETURNTRANSFER => true,\n  CURLOPT_TIMEOUT => 10,\n  CURLOPT_HTTPHEADER => [\"x-api-key: \" . getenv(\"REVKEEN_API_KEY\"), \"Content-Type: application/json\"],\n  CURLOPT_POSTFIELDS => '{\\\"metadata\\\":{\\\"key\\\":\\\"example\\\"}}',\n]);\n$response = curl_exec($request);\nif ($response === false) throw new RuntimeException(curl_error($request));\n$status = curl_getinfo($request, CURLINFO_HTTP_CODE);\ncurl_close($request);\nif ($status >= 400) throw new RuntimeException(\"RevKeen request failed ($status)\");\necho $response;"},{"lang":"python","label":"Python","source":"import os\n\nimport requests\n\nresponse = requests.patch(\n    \"https://api.revkeen.com/v2/subscriptions/11111111-1111-4111-8111-111111111111\",\n    headers={\"x-api-key\": os.environ[\"REVKEEN_API_KEY\"]},\n    json={\n        \"metadata\": {\n            \"key\": \"example\",\n        },\n    },\n)\n\nresponse.raise_for_status()\nprint(response.json())"},{"lang":"javascript","label":"JavaScript","source":"const credential = process.env.REVKEEN_API_KEY;\nif (!credential) throw new Error(\"Set REVKEEN_API_KEY\");\nconst response = await fetch(\"https://api.revkeen.com/v2/subscriptions/11111111-1111-4111-8111-111111111111\", {\n  method: \"PATCH\",\n  headers: {\n    \"x-api-key\": credential,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n    \"metadata\": {\n      \"key\": \"example\"\n    }\n  }),\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"java","label":"Java","source":"import java.net.URI;\nimport java.net.http.HttpClient;\nimport java.net.http.HttpRequest;\nimport java.net.http.HttpResponse;\n\nHttpClient client = HttpClient.newHttpClient();\n\nHttpRequest request = HttpRequest.newBuilder()\n    .uri(URI.create(\"https://api.revkeen.com/v2/subscriptions/11111111-1111-4111-8111-111111111111\"))\n    .header(\"x-api-key\", System.getenv(\"REVKEEN_API_KEY\"))\n    .header(\"Content-Type\", \"application/json\")\n    .method(\"PATCH\", HttpRequest.BodyPublishers.ofString(\"\"\"\n        {\n          \"metadata\": {\n            \"key\": \"example\"\n          }\n        }\n        \"\"\"))\n    .build();\n\nHttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());\nSystem.out.println(response.body());"},{"lang":"ruby","label":"Ruby","source":"require \"net/http\"\nrequire \"json\"\nrequire \"uri\"\n\nuri = URI(\"https://api.revkeen.com/v2/subscriptions/11111111-1111-4111-8111-111111111111\")\nhttp = Net::HTTP.new(uri.host, uri.port)\nhttp.use_ssl = true\n\nrequest = Net::HTTP::Patch.new(uri)\nrequest[\"x-api-key\"] = ENV[\"REVKEEN_API_KEY\"]\nrequest[\"Content-Type\"] = \"application/json\"\nrequest.body = {\n  \"metadata\" => {\n    \"key\" => \"example\",\n  },\n}.to_json\n\nresponse = http.request(request)\nputs response.body"}],"security":[{"apiKey":["subscriptions:write"]},{"oauth2":["subscriptions:write"]}]}},"/subscriptions/{id}/change-plan":{"post":{"operationId":"subscriptions_change_plan","tags":["Subscriptions"],"x-fern-sdk-group-name":["subscriptions"],"summary":"Change subscription plan","description":"Move an `active`, `trialing` or `past_due` subscription to another recurring price, from now.\n\n**Nothing is charged when the plan changes.** With `prorationBehavior` `next_invoice` (the default, and what `invoice_immediately` means here on versions up to `2026-05-01`; from `RevKeen-Version` `2026-11-02` it means charge the card now, which is not available through the API: `403 charge_now_requires_interactive_session` on a card subscription, `400 charge_now_card_only` otherwise), the difference for the rest of the current period is added to the next renewal invoice, or credited there for a downgrade. Two changes before a renewal are netted. With `none` the plan changes and no proration is added. The renewal amount becomes the new price times the subscription's quantity.\n\nThe new price must bill on the subscription's own interval and in its currency (`interval_mismatch`, `currency_mismatch`), and must have an amount above zero (`price_amount_missing`). See the figures first with `POST /v2/subscriptions/{id}/preview-change-plan`, which uses the same calculation.\n\n**Direct Debit.** A Direct Debit subscription is refused with `409 direct_debit_plan_change_unsupported`: a new amount needs advance notice to the payer, which this operation does not send.\n\nEmits `subscription.plan_changed`. Requires the `subscriptions:write` scope and the `subscription:update` permission. Send an `Idempotency-Key` header to make retries safe.\n\n---\n\n**Related endpoints**\n\n- `POST /subscriptions` — Create a new subscription\n- `GET /subscriptions` — List subscriptions\n- `GET /subscriptions/{id}` — Get subscription by ID\n- `PATCH /subscriptions/{id}` — Update subscription details\n- `POST /subscriptions/{id}/preview-change-plan` — Preview a plan change\n- `POST /subscriptions/{id}/pause` — Pause subscription\n- `POST /subscriptions/{id}/resume` — Resume subscription\n- `POST /subscriptions/{id}/cancel` — Cancel subscription\n\n**Common errors**\n\n- `400 invalid_request` — malformed payload or failed validation.\n- `401 authentication_error` — missing, invalid, expired, or revoked credential. Codes: `authentication_failed`, `invalid_api_key`, `expired_api_key`, `api_key_revoked`, `session_invalid`, `merchant_required`. Carries a `WWW-Authenticate: Bearer` challenge.\n- `403 authorization_error` — the caller is identified but the action is denied. Codes include `insufficient_permissions`, `ip_not_allowed`, `merchant_mismatch`, and `origin_not_allowed`.\n- `404 resource_missing` — the referenced resource does not exist or is not visible to your key.\n- `409 conflict` — Idempotency-Key collision with a different body, or a concurrent state-transition conflict.\n- `429 rate_limit_error` — code `rate_limit_exceeded`. Back off using the `Retry-After` header (whole seconds); `X-RateLimit-Reset` is a UNIX timestamp in seconds.\n\n**Idempotency**\n\nPass an `Idempotency-Key` header (UUID v4 recommended) to make retries safe. Keys are valid for 24 hours; see [the idempotency guide](/docs/fundamentals/idempotency).","parameters":[{"schema":{"type":"string","format":"uuid","description":"Subscription UUID"},"required":true,"description":"Subscription UUID","name":"id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"newPlanId":{"type":"string","format":"uuid"},"prorationBehavior":{"type":"string","enum":["invoice_immediately","next_invoice","none"],"description":"How the price difference is billed. `next_invoice` (and `invoice_immediately`, accepted for compatibility and applied the same way on versions up to `2026-05-01`) adds it to the next renewal invoice as a line; a downgrade credit is carried there, never refunded, and a credit larger than the renewal carries to the following one. `none` changes without proration. On `RevKeen-Version` `2026-11-02` or later, `invoice_immediately` means 'charge the card now', which is a dashboard action and not available through the API: a card subscription gets `403 charge_now_requires_interactive_session` and any other gets `400 charge_now_card_only`."},"effectiveDate":{"type":"string","format":"date-time"},"reason":{"type":"string"}},"required":["newPlanId"]}}}},"responses":{"200":{"description":"Plan changed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubscriptionChangePlanResponse"}}}},"400":{"description":"The plan change was refused; nothing was changed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubscriptionPlanChangeError"}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthenticationError"}}},"headers":{"WWW-Authenticate":{"description":"RFC 6750 Bearer authentication challenge","schema":{"type":"string"},"example":"Bearer realm=\"revkeen-api\""}}},"403":{"description":"Insufficient scope (`subscriptions:write`), the authorization policy denied `subscription:update` for the caller, or (from `RevKeen-Version` `2026-11-02`) `prorationBehavior: invoice_immediately` on a card subscription, which only the dashboard can do","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ForbiddenError"},{"$ref":"#/components/schemas/MerchantAuthorizationDeniedError"},{"$ref":"#/components/schemas/SubscriptionChargeNowInteractiveSessionError"}]}}}},"404":{"description":"Subscription not found (or not yours)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotFoundError"}}}},"409":{"description":"A Direct Debit subscription cannot change plan through the API","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubscriptionPlanChangeConflictError"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RateLimitError"}}},"headers":{"Retry-After":{"description":"Whole seconds until the caller may retry","schema":{"type":"string"},"example":"60"},"X-RateLimit-Limit":{"description":"Request quota for the current window","schema":{"type":"string"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window","schema":{"type":"string"}},"X-RateLimit-Reset":{"description":"UNIX timestamp in seconds when the window resets","schema":{"type":"string"}}}},"503":{"description":"A required audit record could not be written; the action was not performed. Retry later.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthorizationAuditUnavailableError"}}}}},"x-cli-group":"subscriptions","x-cli-name":"change-plan","x-codeSamples":[{"lang":"curl","label":"cURL","source":"curl -X POST \"https://api.revkeen.com/v2/subscriptions/11111111-1111-4111-8111-111111111111/change-plan\" \\\n  -H \"x-api-key: $REVKEEN_API_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"newPlanId\": \"11111111-1111-4111-8111-111111111111\"\n  }'"},{"lang":"typescript","label":"TypeScript (HTTP)","source":"const credential = process.env.REVKEEN_API_KEY;\nif (!credential) throw new Error(\"Set REVKEEN_API_KEY\");\nconst response = await fetch(\"https://api.revkeen.com/v2/subscriptions/11111111-1111-4111-8111-111111111111/change-plan\", {\n  method: \"POST\",\n  headers: {\n    \"x-api-key\": credential,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n    \"newPlanId\": \"11111111-1111-4111-8111-111111111111\"\n  }),\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"go","label":"Go (HTTP)","source":"package main\nimport (\"fmt\"; \"io\"; \"net/http\"; \"os\"; \"strings\"; \"time\")\nfunc main() {\n  req, err := http.NewRequest(\"POST\", \"https://api.revkeen.com/v2/subscriptions/11111111-1111-4111-8111-111111111111/change-plan\", strings.NewReader(\"{\\\"newPlanId\\\":\\\"11111111-1111-4111-8111-111111111111\\\"}\"))\n  if err != nil { panic(err) }\n  req.Header.Set(\"x-api-key\", os.Getenv(\"REVKEEN_API_KEY\"))\n  req.Header.Set(\"Content-Type\", \"application/json\")\n  client := &http.Client{Timeout: 10 * time.Second}\n  response, err := client.Do(req)\n  if err != nil { panic(err) }\n  defer response.Body.Close()\n  if response.StatusCode >= 400 { panic(fmt.Sprintf(\"RevKeen request failed (%d)\", response.StatusCode)) }\n  data, err := io.ReadAll(response.Body)\n  if err != nil { panic(err) }\n  fmt.Println(string(data))\n}"},{"lang":"php","label":"PHP (HTTP)","source":"<?php\n$request = curl_init(\"https://api.revkeen.com/v2/subscriptions/11111111-1111-4111-8111-111111111111/change-plan\");\ncurl_setopt_array($request, [\n  CURLOPT_CUSTOMREQUEST => \"POST\",\n  CURLOPT_RETURNTRANSFER => true,\n  CURLOPT_TIMEOUT => 10,\n  CURLOPT_HTTPHEADER => [\"x-api-key: \" . getenv(\"REVKEEN_API_KEY\"), \"Content-Type: application/json\"],\n  CURLOPT_POSTFIELDS => '{\\\"newPlanId\\\":\\\"11111111-1111-4111-8111-111111111111\\\"}',\n]);\n$response = curl_exec($request);\nif ($response === false) throw new RuntimeException(curl_error($request));\n$status = curl_getinfo($request, CURLINFO_HTTP_CODE);\ncurl_close($request);\nif ($status >= 400) throw new RuntimeException(\"RevKeen request failed ($status)\");\necho $response;"},{"lang":"python","label":"Python","source":"import os\n\nimport requests\n\nresponse = requests.post(\n    \"https://api.revkeen.com/v2/subscriptions/11111111-1111-4111-8111-111111111111/change-plan\",\n    headers={\"x-api-key\": os.environ[\"REVKEEN_API_KEY\"]},\n    json={\n        \"newPlanId\": \"11111111-1111-4111-8111-111111111111\",\n    },\n)\n\nresponse.raise_for_status()\nprint(response.json())"},{"lang":"javascript","label":"JavaScript","source":"const credential = process.env.REVKEEN_API_KEY;\nif (!credential) throw new Error(\"Set REVKEEN_API_KEY\");\nconst response = await fetch(\"https://api.revkeen.com/v2/subscriptions/11111111-1111-4111-8111-111111111111/change-plan\", {\n  method: \"POST\",\n  headers: {\n    \"x-api-key\": credential,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n    \"newPlanId\": \"11111111-1111-4111-8111-111111111111\"\n  }),\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"java","label":"Java","source":"import java.net.URI;\nimport java.net.http.HttpClient;\nimport java.net.http.HttpRequest;\nimport java.net.http.HttpResponse;\n\nHttpClient client = HttpClient.newHttpClient();\n\nHttpRequest request = HttpRequest.newBuilder()\n    .uri(URI.create(\"https://api.revkeen.com/v2/subscriptions/11111111-1111-4111-8111-111111111111/change-plan\"))\n    .header(\"x-api-key\", System.getenv(\"REVKEEN_API_KEY\"))\n    .header(\"Content-Type\", \"application/json\")\n    .method(\"POST\", HttpRequest.BodyPublishers.ofString(\"\"\"\n        {\n          \"newPlanId\": \"11111111-1111-4111-8111-111111111111\"\n        }\n        \"\"\"))\n    .build();\n\nHttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());\nSystem.out.println(response.body());"},{"lang":"ruby","label":"Ruby","source":"require \"net/http\"\nrequire \"json\"\nrequire \"uri\"\n\nuri = URI(\"https://api.revkeen.com/v2/subscriptions/11111111-1111-4111-8111-111111111111/change-plan\")\nhttp = Net::HTTP.new(uri.host, uri.port)\nhttp.use_ssl = true\n\nrequest = Net::HTTP::Post.new(uri)\nrequest[\"x-api-key\"] = ENV[\"REVKEEN_API_KEY\"]\nrequest[\"Content-Type\"] = \"application/json\"\nrequest.body = {\n  \"newPlanId\" => \"11111111-1111-4111-8111-111111111111\",\n}.to_json\n\nresponse = http.request(request)\nputs response.body"}],"security":[{"apiKey":["subscriptions:write"]},{"oauth2":["subscriptions:write"]}]}},"/subscriptions/{id}/preview-change-plan":{"post":{"operationId":"subscriptions_preview_change_plan","tags":["Subscriptions"],"x-fern-sdk-group-name":["subscriptions"],"summary":"Preview a plan change","description":"Show what `POST /v2/subscriptions/{id}/change-plan` would do with the same body: the new renewal amount, this change's proration and what the next renewal invoice would add or credit. **Nothing is changed and nothing is recorded.**\n\nIt uses the same calculation and the same checks as the change, so it is refused for the same reasons (`400`, and `409` for a Direct Debit subscription). The figures are as of the request: a change made later prorates over fewer remaining days.\n\nRequires the `subscriptions:read` scope and the `subscription:read` permission.\n\n---\n\n**Related endpoints**\n\n- `POST /subscriptions` — Create a new subscription\n- `GET /subscriptions` — List subscriptions\n- `GET /subscriptions/{id}` — Get subscription by ID\n- `PATCH /subscriptions/{id}` — Update subscription details\n- `POST /subscriptions/{id}/change-plan` — Change subscription plan\n- `POST /subscriptions/{id}/pause` — Pause subscription\n- `POST /subscriptions/{id}/resume` — Resume subscription\n- `POST /subscriptions/{id}/cancel` — Cancel subscription\n\n**Common errors**\n\n- `400 invalid_request` — malformed payload or failed validation.\n- `401 authentication_error` — missing, invalid, expired, or revoked credential. Codes: `authentication_failed`, `invalid_api_key`, `expired_api_key`, `api_key_revoked`, `session_invalid`, `merchant_required`. Carries a `WWW-Authenticate: Bearer` challenge.\n- `403 authorization_error` — the caller is identified but the action is denied. Codes include `insufficient_permissions`, `ip_not_allowed`, `merchant_mismatch`, and `origin_not_allowed`.\n- `404 resource_missing` — the referenced resource does not exist or is not visible to your key.\n- `409 conflict` — Idempotency-Key collision with a different body, or a concurrent state-transition conflict.\n- `429 rate_limit_error` — code `rate_limit_exceeded`. Back off using the `Retry-After` header (whole seconds); `X-RateLimit-Reset` is a UNIX timestamp in seconds.\n\n**Idempotency**\n\nPass an `Idempotency-Key` header (UUID v4 recommended) to make retries safe. Keys are valid for 24 hours; see [the idempotency guide](/docs/fundamentals/idempotency).","parameters":[{"schema":{"type":"string","format":"uuid","description":"Subscription UUID"},"required":true,"description":"Subscription UUID","name":"id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PreviewSubscriptionPlanChangeInput"}}}},"responses":{"200":{"description":"Plan change preview","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubscriptionChangePlanPreviewResponse"}}}},"400":{"description":"The plan change would be refused","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubscriptionPlanChangeError"}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthenticationError"}}},"headers":{"WWW-Authenticate":{"description":"RFC 6750 Bearer authentication challenge","schema":{"type":"string"},"example":"Bearer realm=\"revkeen-api\""}}},"403":{"description":"Insufficient scope (`subscriptions:read`), or the authorization policy denied `subscription:read` for the caller","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MerchantActionForbiddenError"}}}},"404":{"description":"Subscription not found (or not yours)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotFoundError"}}}},"409":{"description":"A Direct Debit subscription cannot change plan through the API","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubscriptionPlanChangeConflictError"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RateLimitError"}}},"headers":{"Retry-After":{"description":"Whole seconds until the caller may retry","schema":{"type":"string"},"example":"60"},"X-RateLimit-Limit":{"description":"Request quota for the current window","schema":{"type":"string"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window","schema":{"type":"string"}},"X-RateLimit-Reset":{"description":"UNIX timestamp in seconds when the window resets","schema":{"type":"string"}}}},"503":{"description":"A required audit record could not be written; the action was not performed. Retry later.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthorizationAuditUnavailableError"}}}}},"x-cli-group":"subscriptions","x-cli-name":"preview-change-plan","x-codeSamples":[{"lang":"curl","label":"cURL","source":"curl -X POST \"https://api.revkeen.com/v2/subscriptions/11111111-1111-4111-8111-111111111111/preview-change-plan\" \\\n  -H \"x-api-key: $REVKEEN_API_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"newPlanId\": \"11111111-1111-4111-8111-111111111111\"\n  }'"},{"lang":"typescript","label":"TypeScript (HTTP)","source":"const credential = process.env.REVKEEN_API_KEY;\nif (!credential) throw new Error(\"Set REVKEEN_API_KEY\");\nconst response = await fetch(\"https://api.revkeen.com/v2/subscriptions/11111111-1111-4111-8111-111111111111/preview-change-plan\", {\n  method: \"POST\",\n  headers: {\n    \"x-api-key\": credential,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n    \"newPlanId\": \"11111111-1111-4111-8111-111111111111\"\n  }),\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"go","label":"Go (HTTP)","source":"package main\nimport (\"fmt\"; \"io\"; \"net/http\"; \"os\"; \"strings\"; \"time\")\nfunc main() {\n  req, err := http.NewRequest(\"POST\", \"https://api.revkeen.com/v2/subscriptions/11111111-1111-4111-8111-111111111111/preview-change-plan\", strings.NewReader(\"{\\\"newPlanId\\\":\\\"11111111-1111-4111-8111-111111111111\\\"}\"))\n  if err != nil { panic(err) }\n  req.Header.Set(\"x-api-key\", os.Getenv(\"REVKEEN_API_KEY\"))\n  req.Header.Set(\"Content-Type\", \"application/json\")\n  client := &http.Client{Timeout: 10 * time.Second}\n  response, err := client.Do(req)\n  if err != nil { panic(err) }\n  defer response.Body.Close()\n  if response.StatusCode >= 400 { panic(fmt.Sprintf(\"RevKeen request failed (%d)\", response.StatusCode)) }\n  data, err := io.ReadAll(response.Body)\n  if err != nil { panic(err) }\n  fmt.Println(string(data))\n}"},{"lang":"php","label":"PHP (HTTP)","source":"<?php\n$request = curl_init(\"https://api.revkeen.com/v2/subscriptions/11111111-1111-4111-8111-111111111111/preview-change-plan\");\ncurl_setopt_array($request, [\n  CURLOPT_CUSTOMREQUEST => \"POST\",\n  CURLOPT_RETURNTRANSFER => true,\n  CURLOPT_TIMEOUT => 10,\n  CURLOPT_HTTPHEADER => [\"x-api-key: \" . getenv(\"REVKEEN_API_KEY\"), \"Content-Type: application/json\"],\n  CURLOPT_POSTFIELDS => '{\\\"newPlanId\\\":\\\"11111111-1111-4111-8111-111111111111\\\"}',\n]);\n$response = curl_exec($request);\nif ($response === false) throw new RuntimeException(curl_error($request));\n$status = curl_getinfo($request, CURLINFO_HTTP_CODE);\ncurl_close($request);\nif ($status >= 400) throw new RuntimeException(\"RevKeen request failed ($status)\");\necho $response;"},{"lang":"python","label":"Python","source":"import os\n\nimport requests\n\nresponse = requests.post(\n    \"https://api.revkeen.com/v2/subscriptions/11111111-1111-4111-8111-111111111111/preview-change-plan\",\n    headers={\"x-api-key\": os.environ[\"REVKEEN_API_KEY\"]},\n    json={\n        \"newPlanId\": \"11111111-1111-4111-8111-111111111111\",\n    },\n)\n\nresponse.raise_for_status()\nprint(response.json())"},{"lang":"javascript","label":"JavaScript","source":"const credential = process.env.REVKEEN_API_KEY;\nif (!credential) throw new Error(\"Set REVKEEN_API_KEY\");\nconst response = await fetch(\"https://api.revkeen.com/v2/subscriptions/11111111-1111-4111-8111-111111111111/preview-change-plan\", {\n  method: \"POST\",\n  headers: {\n    \"x-api-key\": credential,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n    \"newPlanId\": \"11111111-1111-4111-8111-111111111111\"\n  }),\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"java","label":"Java","source":"import java.net.URI;\nimport java.net.http.HttpClient;\nimport java.net.http.HttpRequest;\nimport java.net.http.HttpResponse;\n\nHttpClient client = HttpClient.newHttpClient();\n\nHttpRequest request = HttpRequest.newBuilder()\n    .uri(URI.create(\"https://api.revkeen.com/v2/subscriptions/11111111-1111-4111-8111-111111111111/preview-change-plan\"))\n    .header(\"x-api-key\", System.getenv(\"REVKEEN_API_KEY\"))\n    .header(\"Content-Type\", \"application/json\")\n    .method(\"POST\", HttpRequest.BodyPublishers.ofString(\"\"\"\n        {\n          \"newPlanId\": \"11111111-1111-4111-8111-111111111111\"\n        }\n        \"\"\"))\n    .build();\n\nHttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());\nSystem.out.println(response.body());"},{"lang":"ruby","label":"Ruby","source":"require \"net/http\"\nrequire \"json\"\nrequire \"uri\"\n\nuri = URI(\"https://api.revkeen.com/v2/subscriptions/11111111-1111-4111-8111-111111111111/preview-change-plan\")\nhttp = Net::HTTP.new(uri.host, uri.port)\nhttp.use_ssl = true\n\nrequest = Net::HTTP::Post.new(uri)\nrequest[\"x-api-key\"] = ENV[\"REVKEEN_API_KEY\"]\nrequest[\"Content-Type\"] = \"application/json\"\nrequest.body = {\n  \"newPlanId\" => \"11111111-1111-4111-8111-111111111111\",\n}.to_json\n\nresponse = http.request(request)\nputs response.body"}],"security":[{"apiKey":["subscriptions:read"]},{"oauth2":["subscriptions:read"]}]}},"/subscriptions/{id}/pause":{"post":{"operationId":"subscriptions_pause","tags":["Subscriptions"],"x-fern-sdk-group-name":["subscriptions"],"summary":"Pause subscription","description":"Pause an `active` or `trialing` subscription, immediately. While it is paused:\n- The subscription status is `paused` and no renewal invoices are generated or collected.\n- Nothing already issued is voided, cancelled or recalled. Invoices that are already open stay open.\n- A Direct Debit subscription also stops collecting: its open invoices are listed in `dd_collection_stopped` and are not collected automatically.\n\nIt takes no parameters (send no body, or an empty object). Resume with `POST /v2/subscriptions/{id}/resume`; a pause does not end by itself.\n\nA subscription in any other status is refused with `400 invalid_status` and nothing is changed. Emits `subscription.paused`.\n\nRequires the `subscriptions:write` scope and the `subscription:pause` permission. Send an `Idempotency-Key` header to make retries safe.\n\n---\n\n**Related endpoints**\n\n- `POST /subscriptions` — Create a new subscription\n- `GET /subscriptions` — List subscriptions\n- `GET /subscriptions/{id}` — Get subscription by ID\n- `PATCH /subscriptions/{id}` — Update subscription details\n- `POST /subscriptions/{id}/change-plan` — Change subscription plan\n- `POST /subscriptions/{id}/preview-change-plan` — Preview a plan change\n- `POST /subscriptions/{id}/resume` — Resume subscription\n- `POST /subscriptions/{id}/cancel` — Cancel subscription\n\n**Common errors**\n\n- `400 invalid_request` — malformed payload or failed validation.\n- `401 authentication_error` — missing, invalid, expired, or revoked credential. Codes: `authentication_failed`, `invalid_api_key`, `expired_api_key`, `api_key_revoked`, `session_invalid`, `merchant_required`. Carries a `WWW-Authenticate: Bearer` challenge.\n- `403 authorization_error` — the caller is identified but the action is denied. Codes include `insufficient_permissions`, `ip_not_allowed`, `merchant_mismatch`, and `origin_not_allowed`.\n- `404 resource_missing` — the referenced resource does not exist or is not visible to your key.\n- `429 rate_limit_error` — code `rate_limit_exceeded`. Back off using the `Retry-After` header (whole seconds); `X-RateLimit-Reset` is a UNIX timestamp in seconds.\n\n**Idempotency**\n\nPass an `Idempotency-Key` header (UUID v4 recommended) to make retries safe. Keys are valid for 24 hours; see [the idempotency guide](/docs/fundamentals/idempotency).","parameters":[{"schema":{"type":"string","format":"uuid","description":"Subscription UUID"},"required":true,"description":"Subscription UUID","name":"id","in":"path"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PauseSubscriptionInput"}}}},"responses":{"200":{"description":"Subscription paused successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubscriptionPauseResponse"}}}},"400":{"description":"The subscription's status does not allow a pause (only active or trialing)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubscriptionStateError"}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthenticationError"}}},"headers":{"WWW-Authenticate":{"description":"RFC 6750 Bearer authentication challenge","schema":{"type":"string"},"example":"Bearer realm=\"revkeen-api\""}}},"403":{"description":"Insufficient scope (`subscriptions:write`), or the authorization policy denied this action for the caller","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MerchantActionForbiddenError"}}}},"404":{"description":"Subscription not found (or not yours)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotFoundError"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RateLimitError"}}},"headers":{"Retry-After":{"description":"Whole seconds until the caller may retry","schema":{"type":"string"},"example":"60"},"X-RateLimit-Limit":{"description":"Request quota for the current window","schema":{"type":"string"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window","schema":{"type":"string"}},"X-RateLimit-Reset":{"description":"UNIX timestamp in seconds when the window resets","schema":{"type":"string"}}}},"503":{"description":"A required audit record could not be written; the action was not performed. Retry later.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthorizationAuditUnavailableError"}}}}},"x-cli-group":"subscriptions","x-cli-name":"pause","x-codeSamples":[{"lang":"curl","label":"cURL","source":"curl -X POST \"https://api.revkeen.com/v2/subscriptions/11111111-1111-4111-8111-111111111111/pause\" \\\n  -H \"x-api-key: $REVKEEN_API_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{}'"},{"lang":"typescript","label":"TypeScript (HTTP)","source":"const credential = process.env.REVKEEN_API_KEY;\nif (!credential) throw new Error(\"Set REVKEEN_API_KEY\");\nconst response = await fetch(\"https://api.revkeen.com/v2/subscriptions/11111111-1111-4111-8111-111111111111/pause\", {\n  method: \"POST\",\n  headers: {\n    \"x-api-key\": credential,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({}),\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"go","label":"Go (HTTP)","source":"package main\nimport (\"fmt\"; \"io\"; \"net/http\"; \"os\"; \"strings\"; \"time\")\nfunc main() {\n  req, err := http.NewRequest(\"POST\", \"https://api.revkeen.com/v2/subscriptions/11111111-1111-4111-8111-111111111111/pause\", strings.NewReader(\"{}\"))\n  if err != nil { panic(err) }\n  req.Header.Set(\"x-api-key\", os.Getenv(\"REVKEEN_API_KEY\"))\n  req.Header.Set(\"Content-Type\", \"application/json\")\n  client := &http.Client{Timeout: 10 * time.Second}\n  response, err := client.Do(req)\n  if err != nil { panic(err) }\n  defer response.Body.Close()\n  if response.StatusCode >= 400 { panic(fmt.Sprintf(\"RevKeen request failed (%d)\", response.StatusCode)) }\n  data, err := io.ReadAll(response.Body)\n  if err != nil { panic(err) }\n  fmt.Println(string(data))\n}"},{"lang":"php","label":"PHP (HTTP)","source":"<?php\n$request = curl_init(\"https://api.revkeen.com/v2/subscriptions/11111111-1111-4111-8111-111111111111/pause\");\ncurl_setopt_array($request, [\n  CURLOPT_CUSTOMREQUEST => \"POST\",\n  CURLOPT_RETURNTRANSFER => true,\n  CURLOPT_TIMEOUT => 10,\n  CURLOPT_HTTPHEADER => [\"x-api-key: \" . getenv(\"REVKEEN_API_KEY\"), \"Content-Type: application/json\"],\n  CURLOPT_POSTFIELDS => '{}',\n]);\n$response = curl_exec($request);\nif ($response === false) throw new RuntimeException(curl_error($request));\n$status = curl_getinfo($request, CURLINFO_HTTP_CODE);\ncurl_close($request);\nif ($status >= 400) throw new RuntimeException(\"RevKeen request failed ($status)\");\necho $response;"},{"lang":"python","label":"Python","source":"import os\n\nimport requests\n\nresponse = requests.post(\n    \"https://api.revkeen.com/v2/subscriptions/11111111-1111-4111-8111-111111111111/pause\",\n    headers={\"x-api-key\": os.environ[\"REVKEEN_API_KEY\"]},\n    json={},\n)\n\nresponse.raise_for_status()\nprint(response.json())"},{"lang":"javascript","label":"JavaScript","source":"const credential = process.env.REVKEEN_API_KEY;\nif (!credential) throw new Error(\"Set REVKEEN_API_KEY\");\nconst response = await fetch(\"https://api.revkeen.com/v2/subscriptions/11111111-1111-4111-8111-111111111111/pause\", {\n  method: \"POST\",\n  headers: {\n    \"x-api-key\": credential,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({}),\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"java","label":"Java","source":"import java.net.URI;\nimport java.net.http.HttpClient;\nimport java.net.http.HttpRequest;\nimport java.net.http.HttpResponse;\n\nHttpClient client = HttpClient.newHttpClient();\n\nHttpRequest request = HttpRequest.newBuilder()\n    .uri(URI.create(\"https://api.revkeen.com/v2/subscriptions/11111111-1111-4111-8111-111111111111/pause\"))\n    .header(\"x-api-key\", System.getenv(\"REVKEEN_API_KEY\"))\n    .header(\"Content-Type\", \"application/json\")\n    .method(\"POST\", HttpRequest.BodyPublishers.ofString(\"\"\"\n        {}\n        \"\"\"))\n    .build();\n\nHttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());\nSystem.out.println(response.body());"},{"lang":"ruby","label":"Ruby","source":"require \"net/http\"\nrequire \"json\"\nrequire \"uri\"\n\nuri = URI(\"https://api.revkeen.com/v2/subscriptions/11111111-1111-4111-8111-111111111111/pause\")\nhttp = Net::HTTP.new(uri.host, uri.port)\nhttp.use_ssl = true\n\nrequest = Net::HTTP::Post.new(uri)\nrequest[\"x-api-key\"] = ENV[\"REVKEEN_API_KEY\"]\nrequest[\"Content-Type\"] = \"application/json\"\nrequest.body = {}.to_json\n\nresponse = http.request(request)\nputs response.body"}],"security":[{"apiKey":["subscriptions:write"]},{"oauth2":["subscriptions:write"]}]}},"/subscriptions/{id}/resume":{"post":{"operationId":"subscriptions_resume","tags":["Subscriptions"],"x-fern-sdk-group-name":["subscriptions"],"summary":"Resume subscription","description":"Resume a `paused` subscription. The status returns to `active` and renewals continue.\n\n**The periods that elapsed while it was paused are not billed.** The subscription keeps its billing day and its next charge is the next billing date after the resume, never one charge per paused period.\n\nInvoices that were already open when it was paused stay open; resuming does not collect them.\n\n**Direct Debit.** A Direct Debit subscription resumes only if its mandate can collect now. Otherwise the call is refused with `409 mandate_not_usable` (or `409 direct_debit_not_configured`), the subscription stays paused, and `details.mandate_usability_code` says why. A card subscription has no such check.\n\nA subscription that is not `paused` is refused with `400 invalid_status`. It takes no body. Emits `subscription.resumed`.\n\nRequires the `subscriptions:write` scope and the `subscription:resume` permission. Send an `Idempotency-Key` header to make retries safe.\n\n---\n\n**Related endpoints**\n\n- `POST /subscriptions` — Create a new subscription\n- `GET /subscriptions` — List subscriptions\n- `GET /subscriptions/{id}` — Get subscription by ID\n- `PATCH /subscriptions/{id}` — Update subscription details\n- `POST /subscriptions/{id}/change-plan` — Change subscription plan\n- `POST /subscriptions/{id}/preview-change-plan` — Preview a plan change\n- `POST /subscriptions/{id}/pause` — Pause subscription\n- `POST /subscriptions/{id}/cancel` — Cancel subscription\n\n**Common errors**\n\n- `400 invalid_request` — malformed payload or failed validation.\n- `401 authentication_error` — missing, invalid, expired, or revoked credential. Codes: `authentication_failed`, `invalid_api_key`, `expired_api_key`, `api_key_revoked`, `session_invalid`, `merchant_required`. Carries a `WWW-Authenticate: Bearer` challenge.\n- `403 authorization_error` — the caller is identified but the action is denied. Codes include `insufficient_permissions`, `ip_not_allowed`, `merchant_mismatch`, and `origin_not_allowed`.\n- `404 resource_missing` — the referenced resource does not exist or is not visible to your key.\n- `409 conflict` — Idempotency-Key collision with a different body, or a concurrent state-transition conflict.\n- `429 rate_limit_error` — code `rate_limit_exceeded`. Back off using the `Retry-After` header (whole seconds); `X-RateLimit-Reset` is a UNIX timestamp in seconds.\n\n**Idempotency**\n\nPass an `Idempotency-Key` header (UUID v4 recommended) to make retries safe. Keys are valid for 24 hours; see [the idempotency guide](/docs/fundamentals/idempotency).","parameters":[{"schema":{"type":"string","format":"uuid","description":"Subscription UUID"},"required":true,"description":"Subscription UUID","name":"id","in":"path"}],"responses":{"200":{"description":"Subscription resumed successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubscriptionResumeResponse"}}}},"400":{"description":"The subscription is not paused","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubscriptionStateError"}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthenticationError"}}},"headers":{"WWW-Authenticate":{"description":"RFC 6750 Bearer authentication challenge","schema":{"type":"string"},"example":"Bearer realm=\"revkeen-api\""}}},"403":{"description":"Insufficient scope (`subscriptions:write`), or the authorization policy denied this action for the caller","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MerchantActionForbiddenError"}}}},"404":{"description":"Subscription not found (or not yours)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotFoundError"}}}},"409":{"description":"A Direct Debit subscription whose mandate cannot collect cannot resume","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubscriptionResumeConflictError"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RateLimitError"}}},"headers":{"Retry-After":{"description":"Whole seconds until the caller may retry","schema":{"type":"string"},"example":"60"},"X-RateLimit-Limit":{"description":"Request quota for the current window","schema":{"type":"string"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window","schema":{"type":"string"}},"X-RateLimit-Reset":{"description":"UNIX timestamp in seconds when the window resets","schema":{"type":"string"}}}},"503":{"description":"A required audit record could not be written; the action was not performed. Retry later.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthorizationAuditUnavailableError"}}}}},"x-cli-group":"subscriptions","x-cli-name":"resume","x-codeSamples":[{"lang":"curl","label":"cURL","source":"curl -X POST \"https://api.revkeen.com/v2/subscriptions/11111111-1111-4111-8111-111111111111/resume\" \\\n  -H \"x-api-key: $REVKEEN_API_KEY\""},{"lang":"typescript","label":"TypeScript (HTTP)","source":"const credential = process.env.REVKEEN_API_KEY;\nif (!credential) throw new Error(\"Set REVKEEN_API_KEY\");\nconst response = await fetch(\"https://api.revkeen.com/v2/subscriptions/11111111-1111-4111-8111-111111111111/resume\", {\n  method: \"POST\",\n  headers: {\n    \"x-api-key\": credential,\n  },\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"go","label":"Go (HTTP)","source":"package main\nimport (\"fmt\"; \"io\"; \"net/http\"; \"os\"; \"strings\"; \"time\")\nfunc main() {\n  req, err := http.NewRequest(\"POST\", \"https://api.revkeen.com/v2/subscriptions/11111111-1111-4111-8111-111111111111/resume\", strings.NewReader(\"\"))\n  if err != nil { panic(err) }\n  req.Header.Set(\"x-api-key\", os.Getenv(\"REVKEEN_API_KEY\"))\n  req.Header.Set(\"Content-Type\", \"application/json\")\n  client := &http.Client{Timeout: 10 * time.Second}\n  response, err := client.Do(req)\n  if err != nil { panic(err) }\n  defer response.Body.Close()\n  if response.StatusCode >= 400 { panic(fmt.Sprintf(\"RevKeen request failed (%d)\", response.StatusCode)) }\n  data, err := io.ReadAll(response.Body)\n  if err != nil { panic(err) }\n  fmt.Println(string(data))\n}"},{"lang":"php","label":"PHP (HTTP)","source":"<?php\n$request = curl_init(\"https://api.revkeen.com/v2/subscriptions/11111111-1111-4111-8111-111111111111/resume\");\ncurl_setopt_array($request, [\n  CURLOPT_CUSTOMREQUEST => \"POST\",\n  CURLOPT_RETURNTRANSFER => true,\n  CURLOPT_TIMEOUT => 10,\n  CURLOPT_HTTPHEADER => [\"x-api-key: \" . getenv(\"REVKEEN_API_KEY\"), \"Content-Type: application/json\"],\n]);\n$response = curl_exec($request);\nif ($response === false) throw new RuntimeException(curl_error($request));\n$status = curl_getinfo($request, CURLINFO_HTTP_CODE);\ncurl_close($request);\nif ($status >= 400) throw new RuntimeException(\"RevKeen request failed ($status)\");\necho $response;"},{"lang":"python","label":"Python","source":"import os\n\nimport requests\n\nresponse = requests.post(\n    \"https://api.revkeen.com/v2/subscriptions/11111111-1111-4111-8111-111111111111/resume\",\n    headers={\"x-api-key\": os.environ[\"REVKEEN_API_KEY\"]},\n)\n\nresponse.raise_for_status()\nprint(response.json())"},{"lang":"javascript","label":"JavaScript","source":"const credential = process.env.REVKEEN_API_KEY;\nif (!credential) throw new Error(\"Set REVKEEN_API_KEY\");\nconst response = await fetch(\"https://api.revkeen.com/v2/subscriptions/11111111-1111-4111-8111-111111111111/resume\", {\n  method: \"POST\",\n  headers: {\n    \"x-api-key\": credential,\n  },\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"java","label":"Java","source":"import java.net.URI;\nimport java.net.http.HttpClient;\nimport java.net.http.HttpRequest;\nimport java.net.http.HttpResponse;\n\nHttpClient client = HttpClient.newHttpClient();\n\nHttpRequest request = HttpRequest.newBuilder()\n    .uri(URI.create(\"https://api.revkeen.com/v2/subscriptions/11111111-1111-4111-8111-111111111111/resume\"))\n    .header(\"x-api-key\", System.getenv(\"REVKEEN_API_KEY\"))\n    .method(\"POST\", HttpRequest.BodyPublishers.noBody())\n    .build();\n\nHttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());\nSystem.out.println(response.body());"},{"lang":"ruby","label":"Ruby","source":"require \"net/http\"\nrequire \"json\"\nrequire \"uri\"\n\nuri = URI(\"https://api.revkeen.com/v2/subscriptions/11111111-1111-4111-8111-111111111111/resume\")\nhttp = Net::HTTP.new(uri.host, uri.port)\nhttp.use_ssl = true\n\nrequest = Net::HTTP::Post.new(uri)\nrequest[\"x-api-key\"] = ENV[\"REVKEEN_API_KEY\"]\n\nresponse = http.request(request)\nputs response.body"}],"security":[{"apiKey":["subscriptions:write"]},{"oauth2":["subscriptions:write"]}]}},"/subscriptions/{id}/cancel":{"post":{"operationId":"subscriptions_cancel","tags":["Subscriptions"],"x-fern-sdk-group-name":["subscriptions"],"summary":"Cancel subscription","description":"Cancel a subscription either immediately or at the end of the current billing period.\n\n**Modes:**\n- `immediately`: Status set to \"canceled\", access revoked now\n- `period_end`: cancelAtPeriodEnd flag set, access continues until period end\n\n---\n\n**Related endpoints**\n\n- `POST /subscriptions` — Create a new subscription\n- `GET /subscriptions` — List subscriptions\n- `GET /subscriptions/{id}` — Get subscription by ID\n- `PATCH /subscriptions/{id}` — Update subscription details\n- `POST /subscriptions/{id}/change-plan` — Change subscription plan\n- `POST /subscriptions/{id}/preview-change-plan` — Preview a plan change\n- `POST /subscriptions/{id}/pause` — Pause subscription\n- `POST /subscriptions/{id}/resume` — Resume subscription\n\n**Common errors**\n\n- `400 invalid_request` — malformed payload or failed validation.\n- `401 authentication_error` — missing, invalid, expired, or revoked credential. Codes: `authentication_failed`, `invalid_api_key`, `expired_api_key`, `api_key_revoked`, `session_invalid`, `merchant_required`. Carries a `WWW-Authenticate: Bearer` challenge.\n- `404 resource_missing` — the referenced resource does not exist or is not visible to your key.\n\n**Idempotency**\n\nPass an `Idempotency-Key` header (UUID v4 recommended) to make retries safe. Keys are valid for 24 hours; see [the idempotency guide](/docs/fundamentals/idempotency).","parameters":[{"schema":{"type":"string","format":"uuid","description":"Subscription UUID"},"required":true,"description":"Subscription UUID","name":"id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CancelSubscriptionInput"}}}},"responses":{"200":{"description":"Subscription canceled or cancellation scheduled","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubscriptionCancelSubscriptionResponse"}}}},"400":{"description":"Invalid subscription state for this operation"},"401":{"description":"Unauthorized","headers":{"WWW-Authenticate":{"description":"RFC 6750 Bearer authentication challenge","schema":{"type":"string"},"example":"Bearer realm=\"revkeen-api\""}}},"404":{"description":"Subscription not found"},"429":{"description":"Too Many Requests - Rate limit exceeded","headers":{"Retry-After":{"description":"Whole seconds until the caller may retry","schema":{"type":"string"},"example":"60"},"X-RateLimit-Limit":{"description":"Request quota for the current window","schema":{"type":"string"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window","schema":{"type":"string"}},"X-RateLimit-Reset":{"description":"UNIX timestamp in seconds when the window resets","schema":{"type":"string"}}}}},"x-cli-group":"subscriptions","x-cli-name":"cancel","x-codeSamples":[{"lang":"curl","label":"cURL","source":"curl -X POST \"https://api.revkeen.com/v2/subscriptions/11111111-1111-4111-8111-111111111111/cancel\" \\\n  -H \"x-api-key: $REVKEEN_API_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"mode\": \"immediately\"\n  }'"},{"lang":"typescript","label":"TypeScript (SDK)","source":"// npm install @revkeen/sdk@1.20260822.1541\nimport { RevKeenClient } from \"@revkeen/sdk\";\nconst apiKey = process.env.REVKEEN_API_KEY;\nif (!apiKey) throw new Error(\"Set REVKEEN_API_KEY\");\nconst client = new RevKeenClient({ apiKey, baseUrl: \"https://api.revkeen.com/v2\" });\nconst result = await client.subscriptions.cancel(\"11111111-1111-4111-8111-111111111111\", {\n  \"mode\": \"immediately\"\n});\nconsole.log(result);"},{"lang":"go","label":"Go (HTTP)","source":"package main\nimport (\"fmt\"; \"io\"; \"net/http\"; \"os\"; \"strings\"; \"time\")\nfunc main() {\n  req, err := http.NewRequest(\"POST\", \"https://api.revkeen.com/v2/subscriptions/11111111-1111-4111-8111-111111111111/cancel\", strings.NewReader(\"{\\\"mode\\\":\\\"immediately\\\"}\"))\n  if err != nil { panic(err) }\n  req.Header.Set(\"x-api-key\", os.Getenv(\"REVKEEN_API_KEY\"))\n  req.Header.Set(\"Content-Type\", \"application/json\")\n  client := &http.Client{Timeout: 10 * time.Second}\n  response, err := client.Do(req)\n  if err != nil { panic(err) }\n  defer response.Body.Close()\n  if response.StatusCode >= 400 { panic(fmt.Sprintf(\"RevKeen request failed (%d)\", response.StatusCode)) }\n  data, err := io.ReadAll(response.Body)\n  if err != nil { panic(err) }\n  fmt.Println(string(data))\n}"},{"lang":"php","label":"PHP (HTTP)","source":"<?php\n$request = curl_init(\"https://api.revkeen.com/v2/subscriptions/11111111-1111-4111-8111-111111111111/cancel\");\ncurl_setopt_array($request, [\n  CURLOPT_CUSTOMREQUEST => \"POST\",\n  CURLOPT_RETURNTRANSFER => true,\n  CURLOPT_TIMEOUT => 10,\n  CURLOPT_HTTPHEADER => [\"x-api-key: \" . getenv(\"REVKEEN_API_KEY\"), \"Content-Type: application/json\"],\n  CURLOPT_POSTFIELDS => '{\\\"mode\\\":\\\"immediately\\\"}',\n]);\n$response = curl_exec($request);\nif ($response === false) throw new RuntimeException(curl_error($request));\n$status = curl_getinfo($request, CURLINFO_HTTP_CODE);\ncurl_close($request);\nif ($status >= 400) throw new RuntimeException(\"RevKeen request failed ($status)\");\necho $response;"},{"lang":"python","label":"Python","source":"import os\n\nimport requests\n\nresponse = requests.post(\n    \"https://api.revkeen.com/v2/subscriptions/11111111-1111-4111-8111-111111111111/cancel\",\n    headers={\"x-api-key\": os.environ[\"REVKEEN_API_KEY\"]},\n    json={\n        \"mode\": \"immediately\",\n    },\n)\n\nresponse.raise_for_status()\nprint(response.json())"},{"lang":"javascript","label":"JavaScript","source":"const credential = process.env.REVKEEN_API_KEY;\nif (!credential) throw new Error(\"Set REVKEEN_API_KEY\");\nconst response = await fetch(\"https://api.revkeen.com/v2/subscriptions/11111111-1111-4111-8111-111111111111/cancel\", {\n  method: \"POST\",\n  headers: {\n    \"x-api-key\": credential,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n    \"mode\": \"immediately\"\n  }),\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"java","label":"Java","source":"import java.net.URI;\nimport java.net.http.HttpClient;\nimport java.net.http.HttpRequest;\nimport java.net.http.HttpResponse;\n\nHttpClient client = HttpClient.newHttpClient();\n\nHttpRequest request = HttpRequest.newBuilder()\n    .uri(URI.create(\"https://api.revkeen.com/v2/subscriptions/11111111-1111-4111-8111-111111111111/cancel\"))\n    .header(\"x-api-key\", System.getenv(\"REVKEEN_API_KEY\"))\n    .header(\"Content-Type\", \"application/json\")\n    .method(\"POST\", HttpRequest.BodyPublishers.ofString(\"\"\"\n        {\n          \"mode\": \"immediately\"\n        }\n        \"\"\"))\n    .build();\n\nHttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());\nSystem.out.println(response.body());"},{"lang":"ruby","label":"Ruby","source":"require \"net/http\"\nrequire \"json\"\nrequire \"uri\"\n\nuri = URI(\"https://api.revkeen.com/v2/subscriptions/11111111-1111-4111-8111-111111111111/cancel\")\nhttp = Net::HTTP.new(uri.host, uri.port)\nhttp.use_ssl = true\n\nrequest = Net::HTTP::Post.new(uri)\nrequest[\"x-api-key\"] = ENV[\"REVKEEN_API_KEY\"]\nrequest[\"Content-Type\"] = \"application/json\"\nrequest.body = {\n  \"mode\" => \"immediately\",\n}.to_json\n\nresponse = http.request(request)\nputs response.body"}],"security":[{"apiKey":["subscriptions:write"]},{"oauth2":["subscriptions:write"]}]}},"/refunds":{"get":{"operationId":"refunds_list","tags":["Refunds"],"x-fern-sdk-group-name":["refunds"],"summary":"List refunds","description":"Retrieve a paginated list of refunds with optional filters. Results are ordered by creation date (newest first).\n\n---\n\n**Related endpoints**\n\n- `POST /refunds` — Create a refund\n- `GET /refunds/{id}` — Get refund by ID\n\n**Common errors**\n\n- `401 authentication_error` — missing, invalid, expired, or revoked credential. Codes: `authentication_failed`, `invalid_api_key`, `expired_api_key`, `api_key_revoked`, `session_invalid`, `merchant_required`. Carries a `WWW-Authenticate: Bearer` challenge.\n\n**Pagination**\n\nOffset-based with `limit` (default 25, max 100) and `offset`. The response `pagination` block includes `total` and `hasMore`. See [the pagination guide](/docs/fundamentals/pagination) for SDK auto-paging helpers.","parameters":[{"schema":{"type":"string","enum":["pending","processing","succeeded","failed"],"description":"Filter by refund status"},"required":false,"description":"Filter by refund status","name":"status","in":"query"},{"schema":{"type":"string","format":"uuid","description":"Filter by original payment ID"},"required":false,"description":"Filter by original payment ID","name":"payment_id","in":"query"},{"schema":{"type":"string","description":"Filter by payment gateway (nmi, stripe, etc.)"},"required":false,"description":"Filter by payment gateway (nmi, stripe, etc.)","name":"gateway","in":"query"},{"schema":{"type":"string","enum":["customer_request","duplicate","fraudulent","product_not_received","product_unacceptable","subscription_canceled","other"],"description":"Filter by refund reason"},"required":false,"description":"Filter by refund reason","name":"reason","in":"query"},{"schema":{"type":"string","description":"ISO 8601 date - only refunds created after this date","example":"2024-01-01T00:00:00Z"},"required":false,"description":"ISO 8601 date - only refunds created after this date","name":"created_after","in":"query"},{"schema":{"type":"string","description":"ISO 8601 date - only refunds created before this date","example":"2024-12-31T23:59:59Z"},"required":false,"description":"ISO 8601 date - only refunds created before this date","name":"created_before","in":"query"},{"schema":{"type":"number","minimum":1,"maximum":100,"default":20,"description":"Number of results to return (1-100)","example":20},"required":false,"description":"Number of results to return (1-100)","name":"limit","in":"query"},{"schema":{"type":["number","null"],"minimum":0,"default":0,"description":"Number of results to skip","example":0},"required":false,"description":"Number of results to skip","name":"offset","in":"query"}],"responses":{"200":{"description":"List of refunds","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RefundListResponse"}}}},"401":{"description":"Unauthorized","headers":{"WWW-Authenticate":{"description":"RFC 6750 Bearer authentication challenge","schema":{"type":"string"},"example":"Bearer realm=\"revkeen-api\""}}},"429":{"description":"Too Many Requests - Rate limit exceeded","headers":{"Retry-After":{"description":"Whole seconds until the caller may retry","schema":{"type":"string"},"example":"60"},"X-RateLimit-Limit":{"description":"Request quota for the current window","schema":{"type":"string"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window","schema":{"type":"string"}},"X-RateLimit-Reset":{"description":"UNIX timestamp in seconds when the window resets","schema":{"type":"string"}}}},"500":{"description":"Internal server error"}},"x-cli-group":"refunds","x-cli-name":"list","x-codeSamples":[{"lang":"curl","label":"cURL","source":"curl -X GET \"https://api.revkeen.com/v2/refunds?status=pending&payment_id=11111111-1111-4111-8111-111111111111\" \\\n  -H \"x-api-key: $REVKEEN_API_KEY\""},{"lang":"typescript","label":"TypeScript (SDK)","source":"// npm install @revkeen/sdk@1.20260822.1541\nimport { RevKeenClient } from \"@revkeen/sdk\";\nconst apiKey = process.env.REVKEEN_API_KEY;\nif (!apiKey) throw new Error(\"Set REVKEEN_API_KEY\");\nconst client = new RevKeenClient({ apiKey, baseUrl: \"https://api.revkeen.com/v2\" });\nconst result = await client.refunds.list({\n  \"status\": \"pending\",\n  \"payment_id\": \"11111111-1111-4111-8111-111111111111\"\n});\nconsole.log(result);"},{"lang":"go","label":"Go (HTTP)","source":"package main\nimport (\"fmt\"; \"io\"; \"net/http\"; \"os\"; \"strings\"; \"time\")\nfunc main() {\n  req, err := http.NewRequest(\"GET\", \"https://api.revkeen.com/v2/refunds?status=pending&payment_id=11111111-1111-4111-8111-111111111111\", strings.NewReader(\"\"))\n  if err != nil { panic(err) }\n  req.Header.Set(\"x-api-key\", os.Getenv(\"REVKEEN_API_KEY\"))\n  req.Header.Set(\"Content-Type\", \"application/json\")\n  client := &http.Client{Timeout: 10 * time.Second}\n  response, err := client.Do(req)\n  if err != nil { panic(err) }\n  defer response.Body.Close()\n  if response.StatusCode >= 400 { panic(fmt.Sprintf(\"RevKeen request failed (%d)\", response.StatusCode)) }\n  data, err := io.ReadAll(response.Body)\n  if err != nil { panic(err) }\n  fmt.Println(string(data))\n}"},{"lang":"php","label":"PHP (HTTP)","source":"<?php\n$request = curl_init(\"https://api.revkeen.com/v2/refunds?status=pending&payment_id=11111111-1111-4111-8111-111111111111\");\ncurl_setopt_array($request, [\n  CURLOPT_CUSTOMREQUEST => \"GET\",\n  CURLOPT_RETURNTRANSFER => true,\n  CURLOPT_TIMEOUT => 10,\n  CURLOPT_HTTPHEADER => [\"x-api-key: \" . getenv(\"REVKEEN_API_KEY\"), \"Content-Type: application/json\"],\n]);\n$response = curl_exec($request);\nif ($response === false) throw new RuntimeException(curl_error($request));\n$status = curl_getinfo($request, CURLINFO_HTTP_CODE);\ncurl_close($request);\nif ($status >= 400) throw new RuntimeException(\"RevKeen request failed ($status)\");\necho $response;"},{"lang":"python","label":"Python","source":"import os\n\nimport requests\n\nresponse = requests.get(\n    \"https://api.revkeen.com/v2/refunds\",\n    headers={\"x-api-key\": os.environ[\"REVKEEN_API_KEY\"]},\n    params={\n        \"status\": \"pending\",\n        \"payment_id\": \"11111111-1111-4111-8111-111111111111\",\n    },\n)\n\nresponse.raise_for_status()\nprint(response.json())"},{"lang":"javascript","label":"JavaScript","source":"const credential = process.env.REVKEEN_API_KEY;\nif (!credential) throw new Error(\"Set REVKEEN_API_KEY\");\nconst response = await fetch(\"https://api.revkeen.com/v2/refunds?status=pending&payment_id=11111111-1111-4111-8111-111111111111\", {\n  method: \"GET\",\n  headers: {\n    \"x-api-key\": credential,\n  },\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"java","label":"Java","source":"import java.net.URI;\nimport java.net.http.HttpClient;\nimport java.net.http.HttpRequest;\nimport java.net.http.HttpResponse;\n\nHttpClient client = HttpClient.newHttpClient();\n\nHttpRequest request = HttpRequest.newBuilder()\n    .uri(URI.create(\"https://api.revkeen.com/v2/refunds?status=pending&payment_id=11111111-1111-4111-8111-111111111111\"))\n    .header(\"x-api-key\", System.getenv(\"REVKEEN_API_KEY\"))\n    .GET()\n    .build();\n\nHttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());\nSystem.out.println(response.body());"},{"lang":"ruby","label":"Ruby","source":"require \"net/http\"\nrequire \"json\"\nrequire \"uri\"\n\nuri = URI(\"https://api.revkeen.com/v2/refunds?status=pending&payment_id=11111111-1111-4111-8111-111111111111\")\nhttp = Net::HTTP.new(uri.host, uri.port)\nhttp.use_ssl = true\n\nrequest = Net::HTTP::Get.new(uri)\nrequest[\"x-api-key\"] = ENV[\"REVKEEN_API_KEY\"]\n\nresponse = http.request(request)\nputs response.body"}],"security":[{"apiKey":["refunds:read"]},{"oauth2":["refunds:read"]}]},"post":{"operationId":"refunds_create","tags":["Refunds"],"x-fern-sdk-group-name":["refunds"],"summary":"Create a refund","description":"Initiate a refund for a payment. The refund will be processed through the original payment gateway.\n\n---\n\n**Related endpoints**\n\n- `GET /refunds` — List refunds\n- `GET /refunds/{id}` — Get refund by ID\n\n**Common errors**\n\n- `400 invalid_request` — malformed payload or failed validation.\n- `401 authentication_error` — missing, invalid, expired, or revoked credential. Codes: `authentication_failed`, `invalid_api_key`, `expired_api_key`, `api_key_revoked`, `session_invalid`, `merchant_required`. Carries a `WWW-Authenticate: Bearer` challenge.\n- `403 authorization_error` — the caller is identified but the action is denied. Codes include `insufficient_permissions`, `ip_not_allowed`, `merchant_mismatch`, and `origin_not_allowed`.\n- `404 resource_missing` — the referenced resource does not exist or is not visible to your key.\n- `409 conflict` — Idempotency-Key collision with a different body, or a concurrent state-transition conflict.\n\n**Idempotency**\n\nPass an `Idempotency-Key` header (UUID v4 recommended) to make retries safe. Keys are valid for 24 hours; see [the idempotency guide](/docs/fundamentals/idempotency).","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateRefundInput"},"example":{"payment_id":"660e8400-e29b-41d4-a716-446655440000","amount_minor":5000,"reason":"customer_request","reason_details":"Customer requested refund via support ticket #12345","restock_line_items":[{"invoice_line_item_id":"00000000-0000-0000-0000-000000000000","quantity":1}]}}}},"responses":{"201":{"description":"Refund created successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RefundCreateResponse"}}}},"400":{"description":"Invalid request: `already_refunded` (nothing left to refund), `invalid_amount` (more than the refundable amount, or the payment is not a refundable sale), `invalid_request`, or a Direct Debit payment, which cannot be refunded through the card gateway"},"401":{"description":"Unauthorized","headers":{"WWW-Authenticate":{"description":"RFC 6750 Bearer authentication challenge","schema":{"type":"string"},"example":"Bearer realm=\"revkeen-api\""}}},"403":{"description":"Not allowed to refund this payment (the `payment:refund` permission was denied)"},"404":{"description":"`payment_not_found` (no payment with this id for the merchant) or `resource_missing` (no such payment intent)"},"409":{"description":"`payment_intent_not_charged`: the payment intent has no sale to refund"},"429":{"description":"Too Many Requests - Rate limit exceeded","headers":{"Retry-After":{"description":"Whole seconds until the caller may retry","schema":{"type":"string"},"example":"60"},"X-RateLimit-Limit":{"description":"Request quota for the current window","schema":{"type":"string"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window","schema":{"type":"string"}},"X-RateLimit-Reset":{"description":"UNIX timestamp in seconds when the window resets","schema":{"type":"string"}}}},"500":{"description":"Internal server error"},"503":{"description":"`audit_unavailable` (nothing was created) or `enqueue_failed` (the refund was recorded as failed and not processed)"}},"x-cli-group":"refunds","x-cli-name":"create","x-codeSamples":[{"lang":"curl","label":"cURL","source":"curl -X POST \"https://api.revkeen.com/v2/refunds\" \\\n  -H \"x-api-key: $REVKEEN_API_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"payment_id\": \"11111111-1111-4111-8111-111111111111\",\n    \"amount_minor\": 5000,\n    \"reason\": \"customer_request\",\n    \"reason_details\": \"Customer requested refund via support ticket #12345\"\n  }'"},{"lang":"typescript","label":"TypeScript (SDK)","source":"// npm install @revkeen/sdk@1.20260822.1541\nimport { RevKeenClient } from \"@revkeen/sdk\";\nconst apiKey = process.env.REVKEEN_API_KEY;\nif (!apiKey) throw new Error(\"Set REVKEEN_API_KEY\");\nconst client = new RevKeenClient({ apiKey, baseUrl: \"https://api.revkeen.com/v2\" });\nconst result = await client.refunds.create({\n  \"payment_id\": \"11111111-1111-4111-8111-111111111111\",\n  \"amount_minor\": 5000,\n  \"reason\": \"customer_request\",\n  \"reason_details\": \"Customer requested refund via support ticket #12345\"\n});\nconsole.log(result);"},{"lang":"go","label":"Go (HTTP)","source":"package main\nimport (\"fmt\"; \"io\"; \"net/http\"; \"os\"; \"strings\"; \"time\")\nfunc main() {\n  req, err := http.NewRequest(\"POST\", \"https://api.revkeen.com/v2/refunds\", strings.NewReader(\"{\\\"payment_id\\\":\\\"11111111-1111-4111-8111-111111111111\\\",\\\"amount_minor\\\":5000,\\\"reason\\\":\\\"customer_request\\\",\\\"reason_details\\\":\\\"Customer requested refund via support ticket #12345\\\"}\"))\n  if err != nil { panic(err) }\n  req.Header.Set(\"x-api-key\", os.Getenv(\"REVKEEN_API_KEY\"))\n  req.Header.Set(\"Content-Type\", \"application/json\")\n  client := &http.Client{Timeout: 10 * time.Second}\n  response, err := client.Do(req)\n  if err != nil { panic(err) }\n  defer response.Body.Close()\n  if response.StatusCode >= 400 { panic(fmt.Sprintf(\"RevKeen request failed (%d)\", response.StatusCode)) }\n  data, err := io.ReadAll(response.Body)\n  if err != nil { panic(err) }\n  fmt.Println(string(data))\n}"},{"lang":"php","label":"PHP (HTTP)","source":"<?php\n$request = curl_init(\"https://api.revkeen.com/v2/refunds\");\ncurl_setopt_array($request, [\n  CURLOPT_CUSTOMREQUEST => \"POST\",\n  CURLOPT_RETURNTRANSFER => true,\n  CURLOPT_TIMEOUT => 10,\n  CURLOPT_HTTPHEADER => [\"x-api-key: \" . getenv(\"REVKEEN_API_KEY\"), \"Content-Type: application/json\"],\n  CURLOPT_POSTFIELDS => '{\\\"payment_id\\\":\\\"11111111-1111-4111-8111-111111111111\\\",\\\"amount_minor\\\":5000,\\\"reason\\\":\\\"customer_request\\\",\\\"reason_details\\\":\\\"Customer requested refund via support ticket #12345\\\"}',\n]);\n$response = curl_exec($request);\nif ($response === false) throw new RuntimeException(curl_error($request));\n$status = curl_getinfo($request, CURLINFO_HTTP_CODE);\ncurl_close($request);\nif ($status >= 400) throw new RuntimeException(\"RevKeen request failed ($status)\");\necho $response;"},{"lang":"python","label":"Python","source":"import os\n\nimport requests\n\nresponse = requests.post(\n    \"https://api.revkeen.com/v2/refunds\",\n    headers={\"x-api-key\": os.environ[\"REVKEEN_API_KEY\"]},\n    json={\n        \"payment_id\": \"11111111-1111-4111-8111-111111111111\",\n        \"amount_minor\": 5000,\n        \"reason\": \"customer_request\",\n        \"reason_details\": \"Customer requested refund via support ticket #12345\",\n    },\n)\n\nresponse.raise_for_status()\nprint(response.json())"},{"lang":"javascript","label":"JavaScript","source":"const credential = process.env.REVKEEN_API_KEY;\nif (!credential) throw new Error(\"Set REVKEEN_API_KEY\");\nconst response = await fetch(\"https://api.revkeen.com/v2/refunds\", {\n  method: \"POST\",\n  headers: {\n    \"x-api-key\": credential,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n    \"payment_id\": \"11111111-1111-4111-8111-111111111111\",\n    \"amount_minor\": 5000,\n    \"reason\": \"customer_request\",\n    \"reason_details\": \"Customer requested refund via support ticket #12345\"\n  }),\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"java","label":"Java","source":"import java.net.URI;\nimport java.net.http.HttpClient;\nimport java.net.http.HttpRequest;\nimport java.net.http.HttpResponse;\n\nHttpClient client = HttpClient.newHttpClient();\n\nHttpRequest request = HttpRequest.newBuilder()\n    .uri(URI.create(\"https://api.revkeen.com/v2/refunds\"))\n    .header(\"x-api-key\", System.getenv(\"REVKEEN_API_KEY\"))\n    .header(\"Content-Type\", \"application/json\")\n    .method(\"POST\", HttpRequest.BodyPublishers.ofString(\"\"\"\n        {\n          \"payment_id\": \"11111111-1111-4111-8111-111111111111\",\n          \"amount_minor\": 5000,\n          \"reason\": \"customer_request\",\n          \"reason_details\": \"Customer requested refund via support ticket #12345\"\n        }\n        \"\"\"))\n    .build();\n\nHttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());\nSystem.out.println(response.body());"},{"lang":"ruby","label":"Ruby","source":"require \"net/http\"\nrequire \"json\"\nrequire \"uri\"\n\nuri = URI(\"https://api.revkeen.com/v2/refunds\")\nhttp = Net::HTTP.new(uri.host, uri.port)\nhttp.use_ssl = true\n\nrequest = Net::HTTP::Post.new(uri)\nrequest[\"x-api-key\"] = ENV[\"REVKEEN_API_KEY\"]\nrequest[\"Content-Type\"] = \"application/json\"\nrequest.body = {\n  \"payment_id\" => \"11111111-1111-4111-8111-111111111111\",\n  \"amount_minor\" => 5000,\n  \"reason\" => \"customer_request\",\n  \"reason_details\" => \"Customer requested refund via support ticket #12345\",\n}.to_json\n\nresponse = http.request(request)\nputs response.body"}],"security":[{"apiKey":["refunds:write"]},{"oauth2":["refunds:write"]}]}},"/refunds/{id}":{"get":{"operationId":"refunds_get","tags":["Refunds"],"x-fern-sdk-group-name":["refunds"],"summary":"Get refund by ID","description":"Retrieve a single refund by its public ID (ref_xxx) or internal UUID.\n\n---\n\n**Related endpoints**\n\n- `GET /refunds` — List refunds\n- `POST /refunds` — Create a refund\n\n**Common errors**\n\n- `400 invalid_request` — malformed payload or failed validation.\n- `401 authentication_error` — missing, invalid, expired, or revoked credential. Codes: `authentication_failed`, `invalid_api_key`, `expired_api_key`, `api_key_revoked`, `session_invalid`, `merchant_required`. Carries a `WWW-Authenticate: Bearer` challenge.\n- `404 resource_missing` — the referenced resource does not exist or is not visible to your key.","parameters":[{"schema":{"type":"string","pattern":"^(?:ref_[A-Za-z0-9]+|[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12})$","description":"Refund public ID (`ref_…`) or UUID.","example":"ref_abc123"},"required":true,"description":"Refund public ID (`ref_…`) or UUID.","name":"id","in":"path"}],"responses":{"200":{"description":"Refund details","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RefundRetrieveResponse"}}}},"400":{"description":"`id` is neither a `ref_…` public ID nor a UUID","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RefundRetrieveValidationError"}}}},"401":{"description":"Unauthorized","headers":{"WWW-Authenticate":{"description":"RFC 6750 Bearer authentication challenge","schema":{"type":"string"},"example":"Bearer realm=\"revkeen-api\""}}},"404":{"description":"Refund not found"},"429":{"description":"Too Many Requests - Rate limit exceeded","headers":{"Retry-After":{"description":"Whole seconds until the caller may retry","schema":{"type":"string"},"example":"60"},"X-RateLimit-Limit":{"description":"Request quota for the current window","schema":{"type":"string"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window","schema":{"type":"string"}},"X-RateLimit-Reset":{"description":"UNIX timestamp in seconds when the window resets","schema":{"type":"string"}}}}},"x-cli-group":"refunds","x-cli-name":"get","x-codeSamples":[{"lang":"curl","label":"cURL","source":"curl -X GET \"https://api.revkeen.com/v2/refunds/ref_abc123\" \\\n  -H \"x-api-key: $REVKEEN_API_KEY\""},{"lang":"typescript","label":"TypeScript (SDK)","source":"// npm install @revkeen/sdk@1.20260822.1541\nimport { RevKeenClient } from \"@revkeen/sdk\";\nconst apiKey = process.env.REVKEEN_API_KEY;\nif (!apiKey) throw new Error(\"Set REVKEEN_API_KEY\");\nconst client = new RevKeenClient({ apiKey, baseUrl: \"https://api.revkeen.com/v2\" });\nconst result = await client.refunds.get(\"ref_abc123\");\nconsole.log(result);"},{"lang":"go","label":"Go (HTTP)","source":"package main\nimport (\"fmt\"; \"io\"; \"net/http\"; \"os\"; \"strings\"; \"time\")\nfunc main() {\n  req, err := http.NewRequest(\"GET\", \"https://api.revkeen.com/v2/refunds/ref_abc123\", strings.NewReader(\"\"))\n  if err != nil { panic(err) }\n  req.Header.Set(\"x-api-key\", os.Getenv(\"REVKEEN_API_KEY\"))\n  req.Header.Set(\"Content-Type\", \"application/json\")\n  client := &http.Client{Timeout: 10 * time.Second}\n  response, err := client.Do(req)\n  if err != nil { panic(err) }\n  defer response.Body.Close()\n  if response.StatusCode >= 400 { panic(fmt.Sprintf(\"RevKeen request failed (%d)\", response.StatusCode)) }\n  data, err := io.ReadAll(response.Body)\n  if err != nil { panic(err) }\n  fmt.Println(string(data))\n}"},{"lang":"php","label":"PHP (HTTP)","source":"<?php\n$request = curl_init(\"https://api.revkeen.com/v2/refunds/ref_abc123\");\ncurl_setopt_array($request, [\n  CURLOPT_CUSTOMREQUEST => \"GET\",\n  CURLOPT_RETURNTRANSFER => true,\n  CURLOPT_TIMEOUT => 10,\n  CURLOPT_HTTPHEADER => [\"x-api-key: \" . getenv(\"REVKEEN_API_KEY\"), \"Content-Type: application/json\"],\n]);\n$response = curl_exec($request);\nif ($response === false) throw new RuntimeException(curl_error($request));\n$status = curl_getinfo($request, CURLINFO_HTTP_CODE);\ncurl_close($request);\nif ($status >= 400) throw new RuntimeException(\"RevKeen request failed ($status)\");\necho $response;"},{"lang":"python","label":"Python","source":"import os\n\nimport requests\n\nresponse = requests.get(\n    \"https://api.revkeen.com/v2/refunds/ref_abc123\",\n    headers={\"x-api-key\": os.environ[\"REVKEEN_API_KEY\"]},\n)\n\nresponse.raise_for_status()\nprint(response.json())"},{"lang":"javascript","label":"JavaScript","source":"const credential = process.env.REVKEEN_API_KEY;\nif (!credential) throw new Error(\"Set REVKEEN_API_KEY\");\nconst response = await fetch(\"https://api.revkeen.com/v2/refunds/ref_abc123\", {\n  method: \"GET\",\n  headers: {\n    \"x-api-key\": credential,\n  },\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"java","label":"Java","source":"import java.net.URI;\nimport java.net.http.HttpClient;\nimport java.net.http.HttpRequest;\nimport java.net.http.HttpResponse;\n\nHttpClient client = HttpClient.newHttpClient();\n\nHttpRequest request = HttpRequest.newBuilder()\n    .uri(URI.create(\"https://api.revkeen.com/v2/refunds/ref_abc123\"))\n    .header(\"x-api-key\", System.getenv(\"REVKEEN_API_KEY\"))\n    .GET()\n    .build();\n\nHttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());\nSystem.out.println(response.body());"},{"lang":"ruby","label":"Ruby","source":"require \"net/http\"\nrequire \"json\"\nrequire \"uri\"\n\nuri = URI(\"https://api.revkeen.com/v2/refunds/ref_abc123\")\nhttp = Net::HTTP.new(uri.host, uri.port)\nhttp.use_ssl = true\n\nrequest = Net::HTTP::Get.new(uri)\nrequest[\"x-api-key\"] = ENV[\"REVKEEN_API_KEY\"]\n\nresponse = http.request(request)\nputs response.body"}],"security":[{"apiKey":["refunds:read"]},{"oauth2":["refunds:read"]}]}},"/credit_notes":{"get":{"operationId":"credit_notes_list","tags":["Credit Notes"],"x-fern-sdk-group-name":["creditNotes"],"summary":"List credit notes","description":"Retrieve a paginated list of credit notes with optional filters. Results are ordered by creation date (newest first).\n\n---\n\n**Related endpoints**\n\n- `POST /credit_notes` — Create a credit note\n- `GET /credit_notes/{id}` — Get credit note by ID\n- `POST /credit_notes/{id}/void` — Void a credit note\n- `GET /credit_notes/{id}/lines` — List line items on a credit note\n- `POST /credit_notes/preview` — Preview a credit note without creating it\n\n**Common errors**\n\n- `401 authentication_error` — missing, invalid, expired, or revoked credential. Codes: `authentication_failed`, `invalid_api_key`, `expired_api_key`, `api_key_revoked`, `session_invalid`, `merchant_required`. Carries a `WWW-Authenticate: Bearer` challenge.\n- `403 authorization_error` — the caller is identified but the action is denied. Codes include `insufficient_permissions`, `ip_not_allowed`, `merchant_mismatch`, and `origin_not_allowed`.\n\n**Pagination**\n\nOffset-based with `limit` (default 25, max 100) and `offset`. The response `pagination` block includes `total` and `hasMore`. See [the pagination guide](/docs/fundamentals/pagination) for SDK auto-paging helpers.","security":[{"apiKey":["credit_notes:read"]}],"parameters":[{"schema":{"type":"string","enum":["draft","pending","issued","applied","voided"],"description":"Filter by credit note status"},"required":false,"description":"Filter by credit note status","name":"status","in":"query"},{"schema":{"type":"string","format":"uuid","description":"Filter by invoice ID"},"required":false,"description":"Filter by invoice ID","name":"invoice_id","in":"query"},{"schema":{"type":"string","format":"uuid","description":"Filter by customer ID"},"required":false,"description":"Filter by customer ID","name":"customer_id","in":"query"},{"schema":{"type":"string","enum":["refund_to_payment_method","customer_balance","external"],"description":"Filter by credit method"},"required":false,"description":"Filter by credit method","name":"credit_method","in":"query"},{"schema":{"type":"string","description":"ISO 8601 date - only credit notes created after this date","example":"2024-01-01T00:00:00Z"},"required":false,"description":"ISO 8601 date - only credit notes created after this date","name":"created_after","in":"query"},{"schema":{"type":"string","description":"ISO 8601 date - only credit notes created before this date","example":"2024-12-31T23:59:59Z"},"required":false,"description":"ISO 8601 date - only credit notes created before this date","name":"created_before","in":"query"},{"schema":{"type":"number","minimum":1,"maximum":100,"default":20,"description":"Number of results to return (1-100)","example":20},"required":false,"description":"Number of results to return (1-100)","name":"limit","in":"query"},{"schema":{"type":["number","null"],"minimum":0,"default":0,"description":"Number of results to skip","example":0},"required":false,"description":"Number of results to skip","name":"offset","in":"query"}],"responses":{"200":{"description":"List of credit notes","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreditNoteListResponse"}}}},"401":{"description":"Unauthorized","headers":{"WWW-Authenticate":{"description":"RFC 6750 Bearer authentication challenge","schema":{"type":"string"},"example":"Bearer realm=\"revkeen-api\""}}},"403":{"description":"Forbidden - Insufficient permissions","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ForbiddenError"}}}},"429":{"description":"Too Many Requests - Rate limit exceeded","headers":{"Retry-After":{"description":"Whole seconds until the caller may retry","schema":{"type":"string"},"example":"60"},"X-RateLimit-Limit":{"description":"Request quota for the current window","schema":{"type":"string"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window","schema":{"type":"string"}},"X-RateLimit-Reset":{"description":"UNIX timestamp in seconds when the window resets","schema":{"type":"string"}}}},"500":{"description":"Internal server error"}},"x-cli-group":"credit-notes","x-cli-name":"list","x-codeSamples":[{"lang":"curl","label":"cURL","source":"curl -X GET \"https://api.revkeen.com/v2/credit_notes?status=draft&invoice_id=11111111-1111-4111-8111-111111111111\" \\\n  -H \"x-api-key: $REVKEEN_API_KEY\""},{"lang":"typescript","label":"TypeScript (SDK)","source":"// npm install @revkeen/sdk@1.20260822.1541\nimport { RevKeenClient } from \"@revkeen/sdk\";\nconst apiKey = process.env.REVKEEN_API_KEY;\nif (!apiKey) throw new Error(\"Set REVKEEN_API_KEY\");\nconst client = new RevKeenClient({ apiKey, baseUrl: \"https://api.revkeen.com/v2\" });\nconst result = await client.creditNotes.list({\n  \"status\": \"draft\",\n  \"invoice_id\": \"11111111-1111-4111-8111-111111111111\"\n});\nconsole.log(result);"},{"lang":"go","label":"Go (HTTP)","source":"package main\nimport (\"fmt\"; \"io\"; \"net/http\"; \"os\"; \"strings\"; \"time\")\nfunc main() {\n  req, err := http.NewRequest(\"GET\", \"https://api.revkeen.com/v2/credit_notes?status=draft&invoice_id=11111111-1111-4111-8111-111111111111\", strings.NewReader(\"\"))\n  if err != nil { panic(err) }\n  req.Header.Set(\"x-api-key\", os.Getenv(\"REVKEEN_API_KEY\"))\n  req.Header.Set(\"Content-Type\", \"application/json\")\n  client := &http.Client{Timeout: 10 * time.Second}\n  response, err := client.Do(req)\n  if err != nil { panic(err) }\n  defer response.Body.Close()\n  if response.StatusCode >= 400 { panic(fmt.Sprintf(\"RevKeen request failed (%d)\", response.StatusCode)) }\n  data, err := io.ReadAll(response.Body)\n  if err != nil { panic(err) }\n  fmt.Println(string(data))\n}"},{"lang":"php","label":"PHP (HTTP)","source":"<?php\n$request = curl_init(\"https://api.revkeen.com/v2/credit_notes?status=draft&invoice_id=11111111-1111-4111-8111-111111111111\");\ncurl_setopt_array($request, [\n  CURLOPT_CUSTOMREQUEST => \"GET\",\n  CURLOPT_RETURNTRANSFER => true,\n  CURLOPT_TIMEOUT => 10,\n  CURLOPT_HTTPHEADER => [\"x-api-key: \" . getenv(\"REVKEEN_API_KEY\"), \"Content-Type: application/json\"],\n]);\n$response = curl_exec($request);\nif ($response === false) throw new RuntimeException(curl_error($request));\n$status = curl_getinfo($request, CURLINFO_HTTP_CODE);\ncurl_close($request);\nif ($status >= 400) throw new RuntimeException(\"RevKeen request failed ($status)\");\necho $response;"},{"lang":"python","label":"Python","source":"import os\n\nimport requests\n\nresponse = requests.get(\n    \"https://api.revkeen.com/v2/credit_notes\",\n    headers={\"x-api-key\": os.environ[\"REVKEEN_API_KEY\"]},\n    params={\n        \"status\": \"draft\",\n        \"invoice_id\": \"11111111-1111-4111-8111-111111111111\",\n    },\n)\n\nresponse.raise_for_status()\nprint(response.json())"},{"lang":"javascript","label":"JavaScript","source":"const credential = process.env.REVKEEN_API_KEY;\nif (!credential) throw new Error(\"Set REVKEEN_API_KEY\");\nconst response = await fetch(\"https://api.revkeen.com/v2/credit_notes?status=draft&invoice_id=11111111-1111-4111-8111-111111111111\", {\n  method: \"GET\",\n  headers: {\n    \"x-api-key\": credential,\n  },\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"java","label":"Java","source":"import java.net.URI;\nimport java.net.http.HttpClient;\nimport java.net.http.HttpRequest;\nimport java.net.http.HttpResponse;\n\nHttpClient client = HttpClient.newHttpClient();\n\nHttpRequest request = HttpRequest.newBuilder()\n    .uri(URI.create(\"https://api.revkeen.com/v2/credit_notes?status=draft&invoice_id=11111111-1111-4111-8111-111111111111\"))\n    .header(\"x-api-key\", System.getenv(\"REVKEEN_API_KEY\"))\n    .GET()\n    .build();\n\nHttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());\nSystem.out.println(response.body());"},{"lang":"ruby","label":"Ruby","source":"require \"net/http\"\nrequire \"json\"\nrequire \"uri\"\n\nuri = URI(\"https://api.revkeen.com/v2/credit_notes?status=draft&invoice_id=11111111-1111-4111-8111-111111111111\")\nhttp = Net::HTTP.new(uri.host, uri.port)\nhttp.use_ssl = true\n\nrequest = Net::HTTP::Get.new(uri)\nrequest[\"x-api-key\"] = ENV[\"REVKEEN_API_KEY\"]\n\nresponse = http.request(request)\nputs response.body"}]},"post":{"operationId":"credit_notes_create","tags":["Credit Notes"],"x-fern-sdk-group-name":["creditNotes"],"summary":"Create a credit note","description":"Issue a credit note for a paid or partially paid invoice. The credit can be applied via refund to payment method, customer balance, or marked as external.\n\n---\n\n**Related endpoints**\n\n- `GET /credit_notes` — List credit notes\n- `GET /credit_notes/{id}` — Get credit note by ID\n- `POST /credit_notes/{id}/void` — Void a credit note\n- `GET /credit_notes/{id}/lines` — List line items on a credit note\n- `POST /credit_notes/preview` — Preview a credit note without creating it\n\n**Common errors**\n\n- `400 invalid_request` — malformed payload or failed validation.\n- `401 authentication_error` — missing, invalid, expired, or revoked credential. Codes: `authentication_failed`, `invalid_api_key`, `expired_api_key`, `api_key_revoked`, `session_invalid`, `merchant_required`. Carries a `WWW-Authenticate: Bearer` challenge.\n- `403 authorization_error` — the caller is identified but the action is denied. Codes include `insufficient_permissions`, `ip_not_allowed`, `merchant_mismatch`, and `origin_not_allowed`.\n- `409 conflict` — Idempotency-Key collision with a different body, or a concurrent state-transition conflict.\n\n**Idempotency**\n\nPass an `Idempotency-Key` header (UUID v4 recommended) to make retries safe. Keys are valid for 24 hours; see [the idempotency guide](/docs/fundamentals/idempotency).","security":[{"apiKey":["credit_notes:write"]}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateCreditNoteInput"}}}},"responses":{"201":{"description":"Credit note created successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreditNoteCreateResponse"}}}},"400":{"description":"Invalid request - invoice not eligible, amount exceeds creditable, or other validation error"},"401":{"description":"Unauthorized","headers":{"WWW-Authenticate":{"description":"RFC 6750 Bearer authentication challenge","schema":{"type":"string"},"example":"Bearer realm=\"revkeen-api\""}}},"403":{"description":"Forbidden - Insufficient permissions","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ForbiddenError"}}}},"409":{"description":"Duplicate idempotency key"},"429":{"description":"Too Many Requests - Rate limit exceeded","headers":{"Retry-After":{"description":"Whole seconds until the caller may retry","schema":{"type":"string"},"example":"60"},"X-RateLimit-Limit":{"description":"Request quota for the current window","schema":{"type":"string"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window","schema":{"type":"string"}},"X-RateLimit-Reset":{"description":"UNIX timestamp in seconds when the window resets","schema":{"type":"string"}}}},"500":{"description":"Internal server error"}},"x-cli-group":"credit-notes","x-cli-name":"create","x-codeSamples":[{"lang":"curl","label":"cURL","source":"curl -X POST \"https://api.revkeen.com/v2/credit_notes\" \\\n  -H \"x-api-key: $REVKEEN_API_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"invoice_id\": \"11111111-1111-4111-8111-111111111111\",\n    \"amount_minor\": 5000\n  }'"},{"lang":"typescript","label":"TypeScript (SDK)","source":"// npm install @revkeen/sdk@1.20260822.1541\nimport { RevKeenClient } from \"@revkeen/sdk\";\nconst apiKey = process.env.REVKEEN_API_KEY;\nif (!apiKey) throw new Error(\"Set REVKEEN_API_KEY\");\nconst client = new RevKeenClient({ apiKey, baseUrl: \"https://api.revkeen.com/v2\" });\nconst result = await client.creditNotes.create({\n  \"invoice_id\": \"11111111-1111-4111-8111-111111111111\",\n  \"amount_minor\": 5000\n});\nconsole.log(result);"},{"lang":"go","label":"Go (HTTP)","source":"package main\nimport (\"fmt\"; \"io\"; \"net/http\"; \"os\"; \"strings\"; \"time\")\nfunc main() {\n  req, err := http.NewRequest(\"POST\", \"https://api.revkeen.com/v2/credit_notes\", strings.NewReader(\"{\\\"invoice_id\\\":\\\"11111111-1111-4111-8111-111111111111\\\",\\\"amount_minor\\\":5000}\"))\n  if err != nil { panic(err) }\n  req.Header.Set(\"x-api-key\", os.Getenv(\"REVKEEN_API_KEY\"))\n  req.Header.Set(\"Content-Type\", \"application/json\")\n  client := &http.Client{Timeout: 10 * time.Second}\n  response, err := client.Do(req)\n  if err != nil { panic(err) }\n  defer response.Body.Close()\n  if response.StatusCode >= 400 { panic(fmt.Sprintf(\"RevKeen request failed (%d)\", response.StatusCode)) }\n  data, err := io.ReadAll(response.Body)\n  if err != nil { panic(err) }\n  fmt.Println(string(data))\n}"},{"lang":"php","label":"PHP (HTTP)","source":"<?php\n$request = curl_init(\"https://api.revkeen.com/v2/credit_notes\");\ncurl_setopt_array($request, [\n  CURLOPT_CUSTOMREQUEST => \"POST\",\n  CURLOPT_RETURNTRANSFER => true,\n  CURLOPT_TIMEOUT => 10,\n  CURLOPT_HTTPHEADER => [\"x-api-key: \" . getenv(\"REVKEEN_API_KEY\"), \"Content-Type: application/json\"],\n  CURLOPT_POSTFIELDS => '{\\\"invoice_id\\\":\\\"11111111-1111-4111-8111-111111111111\\\",\\\"amount_minor\\\":5000}',\n]);\n$response = curl_exec($request);\nif ($response === false) throw new RuntimeException(curl_error($request));\n$status = curl_getinfo($request, CURLINFO_HTTP_CODE);\ncurl_close($request);\nif ($status >= 400) throw new RuntimeException(\"RevKeen request failed ($status)\");\necho $response;"},{"lang":"python","label":"Python","source":"import os\n\nimport requests\n\nresponse = requests.post(\n    \"https://api.revkeen.com/v2/credit_notes\",\n    headers={\"x-api-key\": os.environ[\"REVKEEN_API_KEY\"]},\n    json={\n        \"invoice_id\": \"11111111-1111-4111-8111-111111111111\",\n        \"amount_minor\": 5000,\n    },\n)\n\nresponse.raise_for_status()\nprint(response.json())"},{"lang":"javascript","label":"JavaScript","source":"const credential = process.env.REVKEEN_API_KEY;\nif (!credential) throw new Error(\"Set REVKEEN_API_KEY\");\nconst response = await fetch(\"https://api.revkeen.com/v2/credit_notes\", {\n  method: \"POST\",\n  headers: {\n    \"x-api-key\": credential,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n    \"invoice_id\": \"11111111-1111-4111-8111-111111111111\",\n    \"amount_minor\": 5000\n  }),\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"java","label":"Java","source":"import java.net.URI;\nimport java.net.http.HttpClient;\nimport java.net.http.HttpRequest;\nimport java.net.http.HttpResponse;\n\nHttpClient client = HttpClient.newHttpClient();\n\nHttpRequest request = HttpRequest.newBuilder()\n    .uri(URI.create(\"https://api.revkeen.com/v2/credit_notes\"))\n    .header(\"x-api-key\", System.getenv(\"REVKEEN_API_KEY\"))\n    .header(\"Content-Type\", \"application/json\")\n    .method(\"POST\", HttpRequest.BodyPublishers.ofString(\"\"\"\n        {\n          \"invoice_id\": \"11111111-1111-4111-8111-111111111111\",\n          \"amount_minor\": 5000\n        }\n        \"\"\"))\n    .build();\n\nHttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());\nSystem.out.println(response.body());"},{"lang":"ruby","label":"Ruby","source":"require \"net/http\"\nrequire \"json\"\nrequire \"uri\"\n\nuri = URI(\"https://api.revkeen.com/v2/credit_notes\")\nhttp = Net::HTTP.new(uri.host, uri.port)\nhttp.use_ssl = true\n\nrequest = Net::HTTP::Post.new(uri)\nrequest[\"x-api-key\"] = ENV[\"REVKEEN_API_KEY\"]\nrequest[\"Content-Type\"] = \"application/json\"\nrequest.body = {\n  \"invoice_id\" => \"11111111-1111-4111-8111-111111111111\",\n  \"amount_minor\" => 5000,\n}.to_json\n\nresponse = http.request(request)\nputs response.body"}]}},"/credit_notes/{id}":{"get":{"operationId":"credit_notes_get","tags":["Credit Notes"],"x-fern-sdk-group-name":["creditNotes"],"summary":"Get credit note by ID","description":"Retrieve a single credit note by its UUID.\n\n---\n\n**Related endpoints**\n\n- `GET /credit_notes` — List credit notes\n- `POST /credit_notes` — Create a credit note\n- `POST /credit_notes/{id}/void` — Void a credit note\n- `GET /credit_notes/{id}/lines` — List line items on a credit note\n- `POST /credit_notes/preview` — Preview a credit note without creating it\n\n**Common errors**\n\n- `401 authentication_error` — missing, invalid, expired, or revoked credential. Codes: `authentication_failed`, `invalid_api_key`, `expired_api_key`, `api_key_revoked`, `session_invalid`, `merchant_required`. Carries a `WWW-Authenticate: Bearer` challenge.\n- `403 authorization_error` — the caller is identified but the action is denied. Codes include `insufficient_permissions`, `ip_not_allowed`, `merchant_mismatch`, and `origin_not_allowed`.\n- `404 resource_missing` — the referenced resource does not exist or is not visible to your key.","security":[{"apiKey":["credit_notes:read"]}],"parameters":[{"schema":{"type":"string","format":"uuid","description":"Credit note UUID"},"required":true,"description":"Credit note UUID","name":"id","in":"path"}],"responses":{"200":{"description":"Credit note details","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreditNoteRetrieveResponse"}}}},"401":{"description":"Unauthorized","headers":{"WWW-Authenticate":{"description":"RFC 6750 Bearer authentication challenge","schema":{"type":"string"},"example":"Bearer realm=\"revkeen-api\""}}},"403":{"description":"Forbidden - Insufficient permissions","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ForbiddenError"}}}},"404":{"description":"Credit note not found"},"429":{"description":"Too Many Requests - Rate limit exceeded","headers":{"Retry-After":{"description":"Whole seconds until the caller may retry","schema":{"type":"string"},"example":"60"},"X-RateLimit-Limit":{"description":"Request quota for the current window","schema":{"type":"string"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window","schema":{"type":"string"}},"X-RateLimit-Reset":{"description":"UNIX timestamp in seconds when the window resets","schema":{"type":"string"}}}},"500":{"description":"Internal server error"}},"x-cli-group":"credit-notes","x-cli-name":"get","x-codeSamples":[{"lang":"curl","label":"cURL","source":"curl -X GET \"https://api.revkeen.com/v2/credit_notes/11111111-1111-4111-8111-111111111111\" \\\n  -H \"x-api-key: $REVKEEN_API_KEY\""},{"lang":"typescript","label":"TypeScript (SDK)","source":"// npm install @revkeen/sdk@1.20260822.1541\nimport { RevKeenClient } from \"@revkeen/sdk\";\nconst apiKey = process.env.REVKEEN_API_KEY;\nif (!apiKey) throw new Error(\"Set REVKEEN_API_KEY\");\nconst client = new RevKeenClient({ apiKey, baseUrl: \"https://api.revkeen.com/v2\" });\nconst result = await client.creditNotes.get(\"11111111-1111-4111-8111-111111111111\");\nconsole.log(result);"},{"lang":"go","label":"Go (HTTP)","source":"package main\nimport (\"fmt\"; \"io\"; \"net/http\"; \"os\"; \"strings\"; \"time\")\nfunc main() {\n  req, err := http.NewRequest(\"GET\", \"https://api.revkeen.com/v2/credit_notes/11111111-1111-4111-8111-111111111111\", strings.NewReader(\"\"))\n  if err != nil { panic(err) }\n  req.Header.Set(\"x-api-key\", os.Getenv(\"REVKEEN_API_KEY\"))\n  req.Header.Set(\"Content-Type\", \"application/json\")\n  client := &http.Client{Timeout: 10 * time.Second}\n  response, err := client.Do(req)\n  if err != nil { panic(err) }\n  defer response.Body.Close()\n  if response.StatusCode >= 400 { panic(fmt.Sprintf(\"RevKeen request failed (%d)\", response.StatusCode)) }\n  data, err := io.ReadAll(response.Body)\n  if err != nil { panic(err) }\n  fmt.Println(string(data))\n}"},{"lang":"php","label":"PHP (HTTP)","source":"<?php\n$request = curl_init(\"https://api.revkeen.com/v2/credit_notes/11111111-1111-4111-8111-111111111111\");\ncurl_setopt_array($request, [\n  CURLOPT_CUSTOMREQUEST => \"GET\",\n  CURLOPT_RETURNTRANSFER => true,\n  CURLOPT_TIMEOUT => 10,\n  CURLOPT_HTTPHEADER => [\"x-api-key: \" . getenv(\"REVKEEN_API_KEY\"), \"Content-Type: application/json\"],\n]);\n$response = curl_exec($request);\nif ($response === false) throw new RuntimeException(curl_error($request));\n$status = curl_getinfo($request, CURLINFO_HTTP_CODE);\ncurl_close($request);\nif ($status >= 400) throw new RuntimeException(\"RevKeen request failed ($status)\");\necho $response;"},{"lang":"python","label":"Python","source":"import os\n\nimport requests\n\nresponse = requests.get(\n    \"https://api.revkeen.com/v2/credit_notes/11111111-1111-4111-8111-111111111111\",\n    headers={\"x-api-key\": os.environ[\"REVKEEN_API_KEY\"]},\n)\n\nresponse.raise_for_status()\nprint(response.json())"},{"lang":"javascript","label":"JavaScript","source":"const credential = process.env.REVKEEN_API_KEY;\nif (!credential) throw new Error(\"Set REVKEEN_API_KEY\");\nconst response = await fetch(\"https://api.revkeen.com/v2/credit_notes/11111111-1111-4111-8111-111111111111\", {\n  method: \"GET\",\n  headers: {\n    \"x-api-key\": credential,\n  },\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"java","label":"Java","source":"import java.net.URI;\nimport java.net.http.HttpClient;\nimport java.net.http.HttpRequest;\nimport java.net.http.HttpResponse;\n\nHttpClient client = HttpClient.newHttpClient();\n\nHttpRequest request = HttpRequest.newBuilder()\n    .uri(URI.create(\"https://api.revkeen.com/v2/credit_notes/11111111-1111-4111-8111-111111111111\"))\n    .header(\"x-api-key\", System.getenv(\"REVKEEN_API_KEY\"))\n    .GET()\n    .build();\n\nHttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());\nSystem.out.println(response.body());"},{"lang":"ruby","label":"Ruby","source":"require \"net/http\"\nrequire \"json\"\nrequire \"uri\"\n\nuri = URI(\"https://api.revkeen.com/v2/credit_notes/11111111-1111-4111-8111-111111111111\")\nhttp = Net::HTTP.new(uri.host, uri.port)\nhttp.use_ssl = true\n\nrequest = Net::HTTP::Get.new(uri)\nrequest[\"x-api-key\"] = ENV[\"REVKEEN_API_KEY\"]\n\nresponse = http.request(request)\nputs response.body"}]}},"/credit_notes/{id}/void":{"post":{"operationId":"credit_notes_void","tags":["Credit Notes"],"x-fern-sdk-group-name":["creditNotes"],"summary":"Void a credit note","description":"Void a credit note that has been issued but not yet fully applied. This is an accounting void — it reverses the credit without creating a new financial transaction.\n\n---\n\n**Related endpoints**\n\n- `GET /credit_notes` — List credit notes\n- `POST /credit_notes` — Create a credit note\n- `GET /credit_notes/{id}` — Get credit note by ID\n- `GET /credit_notes/{id}/lines` — List line items on a credit note\n- `POST /credit_notes/preview` — Preview a credit note without creating it\n\n**Common errors**\n\n- `400 invalid_request` — malformed payload or failed validation.\n- `401 authentication_error` — missing, invalid, expired, or revoked credential. Codes: `authentication_failed`, `invalid_api_key`, `expired_api_key`, `api_key_revoked`, `session_invalid`, `merchant_required`. Carries a `WWW-Authenticate: Bearer` challenge.\n- `403 authorization_error` — the caller is identified but the action is denied. Codes include `insufficient_permissions`, `ip_not_allowed`, `merchant_mismatch`, and `origin_not_allowed`.\n- `404 resource_missing` — the referenced resource does not exist or is not visible to your key.\n\n**Idempotency**\n\nPass an `Idempotency-Key` header (UUID v4 recommended) to make retries safe. Keys are valid for 24 hours; see [the idempotency guide](/docs/fundamentals/idempotency).","security":[{"apiKey":["credit_notes:write"]}],"parameters":[{"schema":{"type":"string","format":"uuid","description":"Credit note UUID"},"required":true,"description":"Credit note UUID","name":"id","in":"path"}],"responses":{"200":{"description":"Credit note voided successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreditNoteVoidResponse"}}}},"400":{"description":"Credit note cannot be voided (already voided or applied)"},"401":{"description":"Unauthorized","headers":{"WWW-Authenticate":{"description":"RFC 6750 Bearer authentication challenge","schema":{"type":"string"},"example":"Bearer realm=\"revkeen-api\""}}},"403":{"description":"Forbidden - Insufficient permissions","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ForbiddenError"}}}},"404":{"description":"Credit note not found"},"429":{"description":"Too Many Requests - Rate limit exceeded","headers":{"Retry-After":{"description":"Whole seconds until the caller may retry","schema":{"type":"string"},"example":"60"},"X-RateLimit-Limit":{"description":"Request quota for the current window","schema":{"type":"string"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window","schema":{"type":"string"}},"X-RateLimit-Reset":{"description":"UNIX timestamp in seconds when the window resets","schema":{"type":"string"}}}},"500":{"description":"Internal server error"}},"x-cli-group":"credit-notes","x-cli-name":"void","x-codeSamples":[{"lang":"curl","label":"cURL","source":"curl -X POST \"https://api.revkeen.com/v2/credit_notes/11111111-1111-4111-8111-111111111111/void\" \\\n  -H \"x-api-key: $REVKEEN_API_KEY\""},{"lang":"typescript","label":"TypeScript (SDK)","source":"// npm install @revkeen/sdk@1.20260822.1541\nimport { RevKeenClient } from \"@revkeen/sdk\";\nconst apiKey = process.env.REVKEEN_API_KEY;\nif (!apiKey) throw new Error(\"Set REVKEEN_API_KEY\");\nconst client = new RevKeenClient({ apiKey, baseUrl: \"https://api.revkeen.com/v2\" });\nconst result = await client.creditNotes.void(\"11111111-1111-4111-8111-111111111111\");\nconsole.log(result);"},{"lang":"go","label":"Go (HTTP)","source":"package main\nimport (\"fmt\"; \"io\"; \"net/http\"; \"os\"; \"strings\"; \"time\")\nfunc main() {\n  req, err := http.NewRequest(\"POST\", \"https://api.revkeen.com/v2/credit_notes/11111111-1111-4111-8111-111111111111/void\", strings.NewReader(\"\"))\n  if err != nil { panic(err) }\n  req.Header.Set(\"x-api-key\", os.Getenv(\"REVKEEN_API_KEY\"))\n  req.Header.Set(\"Content-Type\", \"application/json\")\n  client := &http.Client{Timeout: 10 * time.Second}\n  response, err := client.Do(req)\n  if err != nil { panic(err) }\n  defer response.Body.Close()\n  if response.StatusCode >= 400 { panic(fmt.Sprintf(\"RevKeen request failed (%d)\", response.StatusCode)) }\n  data, err := io.ReadAll(response.Body)\n  if err != nil { panic(err) }\n  fmt.Println(string(data))\n}"},{"lang":"php","label":"PHP (HTTP)","source":"<?php\n$request = curl_init(\"https://api.revkeen.com/v2/credit_notes/11111111-1111-4111-8111-111111111111/void\");\ncurl_setopt_array($request, [\n  CURLOPT_CUSTOMREQUEST => \"POST\",\n  CURLOPT_RETURNTRANSFER => true,\n  CURLOPT_TIMEOUT => 10,\n  CURLOPT_HTTPHEADER => [\"x-api-key: \" . getenv(\"REVKEEN_API_KEY\"), \"Content-Type: application/json\"],\n]);\n$response = curl_exec($request);\nif ($response === false) throw new RuntimeException(curl_error($request));\n$status = curl_getinfo($request, CURLINFO_HTTP_CODE);\ncurl_close($request);\nif ($status >= 400) throw new RuntimeException(\"RevKeen request failed ($status)\");\necho $response;"},{"lang":"python","label":"Python","source":"import os\n\nimport requests\n\nresponse = requests.post(\n    \"https://api.revkeen.com/v2/credit_notes/11111111-1111-4111-8111-111111111111/void\",\n    headers={\"x-api-key\": os.environ[\"REVKEEN_API_KEY\"]},\n)\n\nresponse.raise_for_status()\nprint(response.json())"},{"lang":"javascript","label":"JavaScript","source":"const credential = process.env.REVKEEN_API_KEY;\nif (!credential) throw new Error(\"Set REVKEEN_API_KEY\");\nconst response = await fetch(\"https://api.revkeen.com/v2/credit_notes/11111111-1111-4111-8111-111111111111/void\", {\n  method: \"POST\",\n  headers: {\n    \"x-api-key\": credential,\n  },\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"java","label":"Java","source":"import java.net.URI;\nimport java.net.http.HttpClient;\nimport java.net.http.HttpRequest;\nimport java.net.http.HttpResponse;\n\nHttpClient client = HttpClient.newHttpClient();\n\nHttpRequest request = HttpRequest.newBuilder()\n    .uri(URI.create(\"https://api.revkeen.com/v2/credit_notes/11111111-1111-4111-8111-111111111111/void\"))\n    .header(\"x-api-key\", System.getenv(\"REVKEEN_API_KEY\"))\n    .method(\"POST\", HttpRequest.BodyPublishers.noBody())\n    .build();\n\nHttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());\nSystem.out.println(response.body());"},{"lang":"ruby","label":"Ruby","source":"require \"net/http\"\nrequire \"json\"\nrequire \"uri\"\n\nuri = URI(\"https://api.revkeen.com/v2/credit_notes/11111111-1111-4111-8111-111111111111/void\")\nhttp = Net::HTTP.new(uri.host, uri.port)\nhttp.use_ssl = true\n\nrequest = Net::HTTP::Post.new(uri)\nrequest[\"x-api-key\"] = ENV[\"REVKEEN_API_KEY\"]\n\nresponse = http.request(request)\nputs response.body"}]}},"/credit_notes/{id}/lines":{"get":{"operationId":"credit_notes_list_lines","tags":["Credit Notes"],"x-fern-sdk-group-name":["creditNotes"],"summary":"List line items on a credit note","description":"Returns the line items attached to a credit note. Each line describes a portion of the invoice that the credit applies to — either a whole invoice line or a prorated slice. Use this to reconstruct the credit's effect per invoice line (for example, when displaying the credit breakdown in your own UI).\n\n---\n\n**Related endpoints**\n\n- `GET /credit_notes` — List credit notes\n- `POST /credit_notes` — Create a credit note\n- `GET /credit_notes/{id}` — Get credit note by ID\n- `POST /credit_notes/{id}/void` — Void a credit note\n- `POST /credit_notes/preview` — Preview a credit note without creating it\n\n**Common errors**\n\n- `401 authentication_error` — missing, invalid, expired, or revoked credential. Codes: `authentication_failed`, `invalid_api_key`, `expired_api_key`, `api_key_revoked`, `session_invalid`, `merchant_required`. Carries a `WWW-Authenticate: Bearer` challenge.\n- `403 authorization_error` — the caller is identified but the action is denied. Codes include `insufficient_permissions`, `ip_not_allowed`, `merchant_mismatch`, and `origin_not_allowed`.\n- `404 resource_missing` — the referenced resource does not exist or is not visible to your key.\n\n**Pagination**\n\nOffset-based with `limit` (default 25, max 100) and `offset`. The response `pagination` block includes `total` and `hasMore`. See [the pagination guide](/docs/fundamentals/pagination) for SDK auto-paging helpers.","security":[{"apiKey":["credit_notes:read"]}],"parameters":[{"schema":{"type":"string","format":"uuid","description":"Credit note UUID"},"required":true,"description":"Credit note UUID","name":"id","in":"path"},{"schema":{"type":"integer","minimum":1,"maximum":100,"default":100,"description":"Maximum number of line items to return (1-100, default 100)."},"required":false,"description":"Maximum number of line items to return (1-100, default 100).","name":"limit","in":"query"}],"responses":{"200":{"description":"Line items on the credit note.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreditNoteLineList"}}}},"401":{"description":"Unauthorized","headers":{"WWW-Authenticate":{"description":"RFC 6750 Bearer authentication challenge","schema":{"type":"string"},"example":"Bearer realm=\"revkeen-api\""}}},"403":{"description":"Forbidden - Insufficient permissions","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ForbiddenError"}}}},"404":{"description":"Credit note not found"},"429":{"description":"Too Many Requests - Rate limit exceeded","headers":{"Retry-After":{"description":"Whole seconds until the caller may retry","schema":{"type":"string"},"example":"60"},"X-RateLimit-Limit":{"description":"Request quota for the current window","schema":{"type":"string"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window","schema":{"type":"string"}},"X-RateLimit-Reset":{"description":"UNIX timestamp in seconds when the window resets","schema":{"type":"string"}}}}},"x-cli-group":"credit-notes","x-cli-name":"list-lines","x-codeSamples":[{"lang":"curl","label":"cURL","source":"curl -X GET \"https://api.revkeen.com/v2/credit_notes/11111111-1111-4111-8111-111111111111/lines?limit=100\" \\\n  -H \"x-api-key: $REVKEEN_API_KEY\""},{"lang":"typescript","label":"TypeScript (SDK)","source":"// npm install @revkeen/sdk@1.20260822.1541\nimport { RevKeenClient } from \"@revkeen/sdk\";\nconst apiKey = process.env.REVKEEN_API_KEY;\nif (!apiKey) throw new Error(\"Set REVKEEN_API_KEY\");\nconst client = new RevKeenClient({ apiKey, baseUrl: \"https://api.revkeen.com/v2\" });\nconst result = await client.creditNotes.listLines(\"11111111-1111-4111-8111-111111111111\", {\n  \"limit\": 100\n});\nconsole.log(result);"},{"lang":"go","label":"Go (HTTP)","source":"package main\nimport (\"fmt\"; \"io\"; \"net/http\"; \"os\"; \"strings\"; \"time\")\nfunc main() {\n  req, err := http.NewRequest(\"GET\", \"https://api.revkeen.com/v2/credit_notes/11111111-1111-4111-8111-111111111111/lines?limit=100\", strings.NewReader(\"\"))\n  if err != nil { panic(err) }\n  req.Header.Set(\"x-api-key\", os.Getenv(\"REVKEEN_API_KEY\"))\n  req.Header.Set(\"Content-Type\", \"application/json\")\n  client := &http.Client{Timeout: 10 * time.Second}\n  response, err := client.Do(req)\n  if err != nil { panic(err) }\n  defer response.Body.Close()\n  if response.StatusCode >= 400 { panic(fmt.Sprintf(\"RevKeen request failed (%d)\", response.StatusCode)) }\n  data, err := io.ReadAll(response.Body)\n  if err != nil { panic(err) }\n  fmt.Println(string(data))\n}"},{"lang":"php","label":"PHP (HTTP)","source":"<?php\n$request = curl_init(\"https://api.revkeen.com/v2/credit_notes/11111111-1111-4111-8111-111111111111/lines?limit=100\");\ncurl_setopt_array($request, [\n  CURLOPT_CUSTOMREQUEST => \"GET\",\n  CURLOPT_RETURNTRANSFER => true,\n  CURLOPT_TIMEOUT => 10,\n  CURLOPT_HTTPHEADER => [\"x-api-key: \" . getenv(\"REVKEEN_API_KEY\"), \"Content-Type: application/json\"],\n]);\n$response = curl_exec($request);\nif ($response === false) throw new RuntimeException(curl_error($request));\n$status = curl_getinfo($request, CURLINFO_HTTP_CODE);\ncurl_close($request);\nif ($status >= 400) throw new RuntimeException(\"RevKeen request failed ($status)\");\necho $response;"},{"lang":"python","label":"Python","source":"import os\n\nimport requests\n\nresponse = requests.get(\n    \"https://api.revkeen.com/v2/credit_notes/11111111-1111-4111-8111-111111111111/lines\",\n    headers={\"x-api-key\": os.environ[\"REVKEEN_API_KEY\"]},\n    params={\n        \"limit\": 100,\n    },\n)\n\nresponse.raise_for_status()\nprint(response.json())"},{"lang":"javascript","label":"JavaScript","source":"const credential = process.env.REVKEEN_API_KEY;\nif (!credential) throw new Error(\"Set REVKEEN_API_KEY\");\nconst response = await fetch(\"https://api.revkeen.com/v2/credit_notes/11111111-1111-4111-8111-111111111111/lines?limit=100\", {\n  method: \"GET\",\n  headers: {\n    \"x-api-key\": credential,\n  },\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"java","label":"Java","source":"import java.net.URI;\nimport java.net.http.HttpClient;\nimport java.net.http.HttpRequest;\nimport java.net.http.HttpResponse;\n\nHttpClient client = HttpClient.newHttpClient();\n\nHttpRequest request = HttpRequest.newBuilder()\n    .uri(URI.create(\"https://api.revkeen.com/v2/credit_notes/11111111-1111-4111-8111-111111111111/lines?limit=100\"))\n    .header(\"x-api-key\", System.getenv(\"REVKEEN_API_KEY\"))\n    .GET()\n    .build();\n\nHttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());\nSystem.out.println(response.body());"},{"lang":"ruby","label":"Ruby","source":"require \"net/http\"\nrequire \"json\"\nrequire \"uri\"\n\nuri = URI(\"https://api.revkeen.com/v2/credit_notes/11111111-1111-4111-8111-111111111111/lines?limit=100\")\nhttp = Net::HTTP.new(uri.host, uri.port)\nhttp.use_ssl = true\n\nrequest = Net::HTTP::Get.new(uri)\nrequest[\"x-api-key\"] = ENV[\"REVKEEN_API_KEY\"]\n\nresponse = http.request(request)\nputs response.body"}]}},"/credit_notes/preview":{"post":{"operationId":"credit_notes_preview","tags":["Credit Notes"],"x-fern-sdk-group-name":["creditNotes"],"summary":"Preview a credit note without creating it","description":"Compute what a credit note would look like against a specific invoice without persisting anything. Use this to validate amounts and show a \"here's what will happen\" UI before the merchant commits. No side effects — no DB writes, no events emitted, no refunds initiated. Mirrors Stripe's `POST /v1/credit_notes/preview`.\n\n---\n\n**Related endpoints**\n\n- `GET /credit_notes` — List credit notes\n- `POST /credit_notes` — Create a credit note\n- `GET /credit_notes/{id}` — Get credit note by ID\n- `POST /credit_notes/{id}/void` — Void a credit note\n- `GET /credit_notes/{id}/lines` — List line items on a credit note\n\n**Common errors**\n\n- `400 invalid_request` — malformed payload or failed validation.\n- `401 authentication_error` — missing, invalid, expired, or revoked credential. Codes: `authentication_failed`, `invalid_api_key`, `expired_api_key`, `api_key_revoked`, `session_invalid`, `merchant_required`. Carries a `WWW-Authenticate: Bearer` challenge.\n- `403 authorization_error` — the caller is identified but the action is denied. Codes include `insufficient_permissions`, `ip_not_allowed`, `merchant_mismatch`, and `origin_not_allowed`.\n- `404 resource_missing` — the referenced resource does not exist or is not visible to your key.\n\n**Idempotency**\n\nPass an `Idempotency-Key` header (UUID v4 recommended) to make retries safe. Keys are valid for 24 hours; see [the idempotency guide](/docs/fundamentals/idempotency).","security":[{"apiKey":["credit_notes:read"]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PreviewCreditNoteInput"}}}},"responses":{"200":{"description":"Preview computed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreditNotePreviewResponse"}}}},"400":{"description":"Invalid preview input"},"401":{"description":"Unauthorized","headers":{"WWW-Authenticate":{"description":"RFC 6750 Bearer authentication challenge","schema":{"type":"string"},"example":"Bearer realm=\"revkeen-api\""}}},"403":{"description":"Forbidden - Insufficient permissions","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ForbiddenError"}}}},"404":{"description":"Invoice not found"},"429":{"description":"Too Many Requests - Rate limit exceeded","headers":{"Retry-After":{"description":"Whole seconds until the caller may retry","schema":{"type":"string"},"example":"60"},"X-RateLimit-Limit":{"description":"Request quota for the current window","schema":{"type":"string"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window","schema":{"type":"string"}},"X-RateLimit-Reset":{"description":"UNIX timestamp in seconds when the window resets","schema":{"type":"string"}}}}},"x-cli-group":"credit-notes","x-cli-name":"preview","x-codeSamples":[{"lang":"curl","label":"cURL","source":"curl -X POST \"https://api.revkeen.com/v2/credit_notes/preview\" \\\n  -H \"x-api-key: $REVKEEN_API_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"invoice_id\": \"11111111-1111-4111-8111-111111111111\",\n    \"amount_minor\": 5000\n  }'"},{"lang":"typescript","label":"TypeScript (SDK)","source":"// npm install @revkeen/sdk@1.20260822.1541\nimport { RevKeenClient } from \"@revkeen/sdk\";\nconst apiKey = process.env.REVKEEN_API_KEY;\nif (!apiKey) throw new Error(\"Set REVKEEN_API_KEY\");\nconst client = new RevKeenClient({ apiKey, baseUrl: \"https://api.revkeen.com/v2\" });\nconst result = await client.creditNotes.preview({\n  \"invoice_id\": \"11111111-1111-4111-8111-111111111111\",\n  \"amount_minor\": 5000\n});\nconsole.log(result);"},{"lang":"go","label":"Go (HTTP)","source":"package main\nimport (\"fmt\"; \"io\"; \"net/http\"; \"os\"; \"strings\"; \"time\")\nfunc main() {\n  req, err := http.NewRequest(\"POST\", \"https://api.revkeen.com/v2/credit_notes/preview\", strings.NewReader(\"{\\\"invoice_id\\\":\\\"11111111-1111-4111-8111-111111111111\\\",\\\"amount_minor\\\":5000}\"))\n  if err != nil { panic(err) }\n  req.Header.Set(\"x-api-key\", os.Getenv(\"REVKEEN_API_KEY\"))\n  req.Header.Set(\"Content-Type\", \"application/json\")\n  client := &http.Client{Timeout: 10 * time.Second}\n  response, err := client.Do(req)\n  if err != nil { panic(err) }\n  defer response.Body.Close()\n  if response.StatusCode >= 400 { panic(fmt.Sprintf(\"RevKeen request failed (%d)\", response.StatusCode)) }\n  data, err := io.ReadAll(response.Body)\n  if err != nil { panic(err) }\n  fmt.Println(string(data))\n}"},{"lang":"php","label":"PHP (HTTP)","source":"<?php\n$request = curl_init(\"https://api.revkeen.com/v2/credit_notes/preview\");\ncurl_setopt_array($request, [\n  CURLOPT_CUSTOMREQUEST => \"POST\",\n  CURLOPT_RETURNTRANSFER => true,\n  CURLOPT_TIMEOUT => 10,\n  CURLOPT_HTTPHEADER => [\"x-api-key: \" . getenv(\"REVKEEN_API_KEY\"), \"Content-Type: application/json\"],\n  CURLOPT_POSTFIELDS => '{\\\"invoice_id\\\":\\\"11111111-1111-4111-8111-111111111111\\\",\\\"amount_minor\\\":5000}',\n]);\n$response = curl_exec($request);\nif ($response === false) throw new RuntimeException(curl_error($request));\n$status = curl_getinfo($request, CURLINFO_HTTP_CODE);\ncurl_close($request);\nif ($status >= 400) throw new RuntimeException(\"RevKeen request failed ($status)\");\necho $response;"},{"lang":"python","label":"Python","source":"import os\n\nimport requests\n\nresponse = requests.post(\n    \"https://api.revkeen.com/v2/credit_notes/preview\",\n    headers={\"x-api-key\": os.environ[\"REVKEEN_API_KEY\"]},\n    json={\n        \"invoice_id\": \"11111111-1111-4111-8111-111111111111\",\n        \"amount_minor\": 5000,\n    },\n)\n\nresponse.raise_for_status()\nprint(response.json())"},{"lang":"javascript","label":"JavaScript","source":"const credential = process.env.REVKEEN_API_KEY;\nif (!credential) throw new Error(\"Set REVKEEN_API_KEY\");\nconst response = await fetch(\"https://api.revkeen.com/v2/credit_notes/preview\", {\n  method: \"POST\",\n  headers: {\n    \"x-api-key\": credential,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n    \"invoice_id\": \"11111111-1111-4111-8111-111111111111\",\n    \"amount_minor\": 5000\n  }),\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"java","label":"Java","source":"import java.net.URI;\nimport java.net.http.HttpClient;\nimport java.net.http.HttpRequest;\nimport java.net.http.HttpResponse;\n\nHttpClient client = HttpClient.newHttpClient();\n\nHttpRequest request = HttpRequest.newBuilder()\n    .uri(URI.create(\"https://api.revkeen.com/v2/credit_notes/preview\"))\n    .header(\"x-api-key\", System.getenv(\"REVKEEN_API_KEY\"))\n    .header(\"Content-Type\", \"application/json\")\n    .method(\"POST\", HttpRequest.BodyPublishers.ofString(\"\"\"\n        {\n          \"invoice_id\": \"11111111-1111-4111-8111-111111111111\",\n          \"amount_minor\": 5000\n        }\n        \"\"\"))\n    .build();\n\nHttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());\nSystem.out.println(response.body());"},{"lang":"ruby","label":"Ruby","source":"require \"net/http\"\nrequire \"json\"\nrequire \"uri\"\n\nuri = URI(\"https://api.revkeen.com/v2/credit_notes/preview\")\nhttp = Net::HTTP.new(uri.host, uri.port)\nhttp.use_ssl = true\n\nrequest = Net::HTTP::Post.new(uri)\nrequest[\"x-api-key\"] = ENV[\"REVKEEN_API_KEY\"]\nrequest[\"Content-Type\"] = \"application/json\"\nrequest.body = {\n  \"invoice_id\" => \"11111111-1111-4111-8111-111111111111\",\n  \"amount_minor\" => 5000,\n}.to_json\n\nresponse = http.request(request)\nputs response.body"}]}},"/payment-links":{"post":{"operationId":"payment_links_create","tags":["Payment Links"],"summary":"Create a new payment link","description":"Create a new payment link for invoices, subscriptions, or custom amounts\n\n---\n\n**Related endpoints**\n\n- `GET /payment-links` — List payment links\n- `GET /payment-links/{id}` — Get payment link by ID\n- `POST /payment-links/{id}/expire` — Expire a payment link\n- `PATCH /payment-links/{id}/status` — Update payment link status\n- `POST /payment-links/{id}/deactivate` — Deactivate a payment link\n- `POST /payment-links/{id}/activate` — Activate a payment link\n- `POST /payment-links/{id}/archive` — Archive a payment link\n\n**Common errors**\n\n- `400 invalid_request` — malformed payload or failed validation.\n- `401 authentication_error` — missing, invalid, expired, or revoked credential. Codes: `authentication_failed`, `invalid_api_key`, `expired_api_key`, `api_key_revoked`, `session_invalid`, `merchant_required`. Carries a `WWW-Authenticate: Bearer` challenge.\n- `409 conflict` — Idempotency-Key collision with a different body, or a concurrent state-transition conflict.\n\n**Idempotency**\n\nPass an `Idempotency-Key` header (UUID v4 recommended) to make retries safe. Keys are valid for 24 hours; see [the idempotency guide](/docs/fundamentals/idempotency).","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"product_ids":{"type":"array","items":{"type":"string","format":"uuid"}},"line_items":{"type":"array","items":{"type":"object","properties":{"price":{"type":"string","format":"uuid"},"price_data":{"type":"object","properties":{"currency":{"type":"string","minLength":3,"maxLength":3,"description":"Three-letter ISO currency code. Defaults to the merchant's default currency."},"unit_amount":{"type":"integer","minimum":0},"product_data":{"type":"object","properties":{"name":{"type":"string","minLength":1},"description":{"type":["string","null"]},"images":{"type":["array","null"],"items":{"type":"string","format":"uri"}},"metadata":{"type":["object","null"],"additionalProperties":{}}},"required":["name"]},"product":{"type":"string","format":"uuid"},"recurring":{"type":["object","null"],"properties":{"interval":{"type":"string","enum":["day","week","month","year"]},"interval_count":{"type":"integer","minimum":1,"default":1}},"required":["interval"]},"tax_behavior":{"type":["string","null"],"enum":["inclusive","exclusive",null]}},"required":["unit_amount"]},"quantity":{"type":"integer","minimum":1,"default":1},"merchant_reference":{"type":["string","null"],"maxLength":255}}}},"name":{"type":"string"},"slug":{"type":"string"},"mode":{"type":"string","enum":["invoice","subscription","custom"],"default":"custom"},"amount_minor":{"type":"integer","minimum":0,"deprecated":true,"description":"Deprecated and ignored. A payment link charges the prices of its products or line_items; an amount sent here is never charged."},"currency":{"type":"string","minLength":3,"maxLength":3,"description":"Not used: a payment link takes its currency from its products. For an ad-hoc line item, set line_items[].price_data.currency."},"invoice_id":{"type":"string","format":"uuid","deprecated":true,"description":"Deprecated and ignored. This endpoint does not create a payment link for an invoice."},"subscription_id":{"type":"string","format":"uuid","deprecated":true,"description":"Deprecated and ignored. This endpoint does not attach a payment link to an existing subscription."},"success_url":{"type":"string","format":"uri","description":"Custom URL to redirect after successful payment. Supports placeholders: {CHECKOUT_ID}, {TRANSACTION_ID}, {PAYMENT_ID}, {CUSTOMER_ID}, {AMOUNT}, {CURRENCY}, {PORTAL_URL}, {CHECKOUT_STATUS_TOKEN}. If not set, redirects to RevKeen's default success page."},"cancel_url":{"type":"string","format":"uri","description":"Custom URL to redirect if customer cancels the checkout. If not set, no cancel redirect is configured."},"redirect_url":{"type":"string","format":"uri","deprecated":true,"description":"Deprecated and ignored. Use success_url."},"collect_shipping":{"type":"boolean","default":false,"description":"When true, checkout asks the customer for a delivery address before payment."},"allow_promotion_codes":{"type":"boolean","default":true,"description":"Not applied yet: links created through this endpoint do not accept discount codes, whatever this is set to."},"tax_behavior":{"type":"string","enum":["exclusive","inclusive","unspecified"],"default":"unspecified","description":"Whether the link's prices include tax. inclusive or exclusive is saved on the link; unspecified keeps the default (exclusive). Tax is only calculated when the link's tax mode is manual or auto, which this endpoint does not set."},"metadata":{"type":"object","additionalProperties":{}},"expires_at":{"type":"string","format":"date-time"},"selection_mode":{"type":"string","enum":["bundle","single_select","multi_select"],"default":"bundle","description":"Customer-facing checkout shape. bundle=all products together (default); single_select=radio picker; multi_select=cart with quantity per line. Companion FKs (default_product_id, highlighted_product_id) are only valid when selection_mode='single_select'."},"default_product_id":{"type":["string","null"],"format":"uuid","description":"For single_select only — option pre-selected when checkout loads. Must reference a product attached to this payment link."},"highlighted_product_id":{"type":["string","null"],"format":"uuid","description":"For single_select only — option shown with a 'Popular' badge in the picker."},"idempotency_key":{"type":"string","deprecated":true,"description":"Deprecated and ignored. Send the Idempotency-Key header: a repeat request with the same header returns the original response."}}}}}},"responses":{"201":{"description":"Payment link created successfully","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"merchant_id":{"type":"string","format":"uuid"},"name":{"type":"string"},"slug":{"type":["string","null"]},"mode":{"type":"string","enum":["invoice","subscription","custom"],"description":"The link's mode. A link created in the dashboard carries a checkout-template value in its stored mode; it is returned here as custom."},"status":{"type":"string","enum":["active","inactive","archived","expired"],"description":"active links accept payments. inactive links are switched off and can be activated again. archived links are permanently disabled. expired links are past their expiry."},"url":{"type":"string","format":"uri","description":"The hosted checkout page for this link. Share this URL with the customer."},"amount_minor":{"type":["integer","null"]},"currency":{"type":"string","minLength":3,"maxLength":3,"description":"Three-letter ISO currency code, taken from the link's products."},"invoice_id":{"type":["string","null"],"format":"uuid"},"subscription_id":{"type":["string","null"],"format":"uuid"},"product_ids":{"type":"array","items":{"type":"string","format":"uuid"}},"line_items":{"type":"array","items":{"type":"object","properties":{"product_id":{"type":"string","format":"uuid"},"price_id":{"type":["string","null"],"format":"uuid"},"quantity":{"type":"integer","minimum":1},"merchant_reference":{"type":["string","null"]},"is_adhoc":{"type":"boolean"},"product_details":{"type":"object","properties":{"name":{"type":"string"},"description":{"type":["string","null"]},"unit_amount":{"type":"integer"},"currency":{"type":"string"},"recurring":{"type":["object","null"],"properties":{"interval":{"type":"string","enum":["day","week","month","year"]},"interval_count":{"type":"integer","minimum":1,"default":1}},"required":["interval"]}},"required":["name","unit_amount","currency"]}},"required":["product_id","quantity","is_adhoc"]}},"success_url":{"type":["string","null"],"format":"uri","description":"Custom URL to redirect after successful payment. Supports placeholders: {CHECKOUT_ID}, {TRANSACTION_ID}, {PAYMENT_ID}, {CUSTOMER_ID}, {AMOUNT}, {CURRENCY}, {PORTAL_URL}, {CHECKOUT_STATUS_TOKEN}. If not set, redirects to RevKeen's default success page."},"cancel_url":{"type":["string","null"],"format":"uri","description":"Custom URL to redirect if customer cancels the checkout. If not set, no cancel redirect is configured."},"redirect_url":{"type":["string","null"],"format":"uri"},"collect_shipping":{"type":"boolean","default":false},"allow_promotion_codes":{"type":"boolean","default":true},"tax_behavior":{"type":"string","enum":["exclusive","inclusive","unspecified"],"default":"unspecified"},"metadata":{"type":["object","null"],"additionalProperties":{}},"expires_at":{"type":["string","null"],"format":"date-time"},"selection_mode":{"type":"string","enum":["bundle","single_select","multi_select"],"default":"bundle","description":"Customer-facing checkout shape. bundle=all products together; single_select=radio picker; multi_select=cart with quantity per line."},"default_product_id":{"type":["string","null"],"format":"uuid","description":"For single_select only — option pre-selected when checkout loads. Must reference a product attached to this payment link."},"highlighted_product_id":{"type":["string","null"],"format":"uuid","description":"For single_select only — option shown with a 'Popular' badge."},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"}},"required":["id","merchant_id","mode","status","url","created_at","updated_at"]}}}},"400":{"description":"Validation error or invalid input","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{}},"required":["error"]}}}},"401":{"description":"Unauthorized - missing or invalid authentication","headers":{"WWW-Authenticate":{"description":"RFC 6750 Bearer authentication challenge","schema":{"type":"string"},"example":"Bearer realm=\"revkeen-api\""}}},"409":{"description":"Conflict - payment link already exists","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{}},"required":["error"]}}}},"429":{"description":"Too Many Requests - Rate limit exceeded","headers":{"Retry-After":{"description":"Whole seconds until the caller may retry","schema":{"type":"string"},"example":"60"},"X-RateLimit-Limit":{"description":"Request quota for the current window","schema":{"type":"string"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window","schema":{"type":"string"}},"X-RateLimit-Reset":{"description":"UNIX timestamp in seconds when the window resets","schema":{"type":"string"}}}}},"x-cli-group":"payment-links","x-cli-name":"create","x-codeSamples":[{"lang":"curl","label":"cURL","source":"curl -X POST \"https://api.revkeen.com/v2/payment-links\" \\\n  -H \"x-api-key: $REVKEEN_API_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"product_ids\": [\n      \"11111111-1111-4111-8111-111111111111\"\n    ],\n    \"line_items\": [\n      {\n        \"price\": \"11111111-1111-4111-8111-111111111111\",\n        \"price_data\": {\n          \"currency\": \"USD\",\n          \"unit_amount\": 1000,\n          \"product_data\": {\n            \"name\": \"example\",\n            \"description\": \"Example request from API docs\",\n            \"images\": [\n              \"https://example.com/success\"\n            ],\n            \"metadata\": {\n              \"key\": \"example\"\n            }\n          },\n          \"product\": \"11111111-1111-4111-8111-111111111111\"\n        },\n        \"quantity\": 1,\n        \"merchant_reference\": \"example-reference\"\n      }\n    ],\n    \"name\": \"example\",\n    \"slug\": \"example\"\n  }'"},{"lang":"typescript","label":"TypeScript (SDK)","source":"// npm install @revkeen/sdk@1.20260822.1541\nimport { RevKeenClient } from \"@revkeen/sdk\";\nconst apiKey = process.env.REVKEEN_API_KEY;\nif (!apiKey) throw new Error(\"Set REVKEEN_API_KEY\");\nconst client = new RevKeenClient({ apiKey, baseUrl: \"https://api.revkeen.com/v2\" });\nconst result = await client.paymentLinks.create({\n  \"product_ids\": [\n    \"11111111-1111-4111-8111-111111111111\"\n  ],\n  \"line_items\": [\n    {\n      \"price\": \"11111111-1111-4111-8111-111111111111\",\n      \"price_data\": {\n        \"currency\": \"USD\",\n        \"unit_amount\": 1000,\n        \"product_data\": {\n          \"name\": \"example\",\n          \"description\": \"Example request from API docs\",\n          \"images\": [\n            \"https://example.com/success\"\n          ],\n          \"metadata\": {\n            \"key\": \"example\"\n          }\n        },\n        \"product\": \"11111111-1111-4111-8111-111111111111\"\n      },\n      \"quantity\": 1,\n      \"merchant_reference\": \"example-reference\"\n    }\n  ],\n  \"name\": \"example\",\n  \"slug\": \"example\"\n});\nconsole.log(result);"},{"lang":"go","label":"Go (HTTP)","source":"package main\nimport (\"fmt\"; \"io\"; \"net/http\"; \"os\"; \"strings\"; \"time\")\nfunc main() {\n  req, err := http.NewRequest(\"POST\", \"https://api.revkeen.com/v2/payment-links\", strings.NewReader(\"{\\\"product_ids\\\":[\\\"11111111-1111-4111-8111-111111111111\\\"],\\\"line_items\\\":[{\\\"price\\\":\\\"11111111-1111-4111-8111-111111111111\\\",\\\"price_data\\\":{\\\"currency\\\":\\\"USD\\\",\\\"unit_amount\\\":1000,\\\"product_data\\\":{\\\"name\\\":\\\"example\\\",\\\"description\\\":\\\"Example request from API docs\\\",\\\"images\\\":[\\\"https://example.com/success\\\"],\\\"metadata\\\":{\\\"key\\\":\\\"example\\\"}},\\\"product\\\":\\\"11111111-1111-4111-8111-111111111111\\\"},\\\"quantity\\\":1,\\\"merchant_reference\\\":\\\"example-reference\\\"}],\\\"name\\\":\\\"example\\\",\\\"slug\\\":\\\"example\\\"}\"))\n  if err != nil { panic(err) }\n  req.Header.Set(\"x-api-key\", os.Getenv(\"REVKEEN_API_KEY\"))\n  req.Header.Set(\"Content-Type\", \"application/json\")\n  client := &http.Client{Timeout: 10 * time.Second}\n  response, err := client.Do(req)\n  if err != nil { panic(err) }\n  defer response.Body.Close()\n  if response.StatusCode >= 400 { panic(fmt.Sprintf(\"RevKeen request failed (%d)\", response.StatusCode)) }\n  data, err := io.ReadAll(response.Body)\n  if err != nil { panic(err) }\n  fmt.Println(string(data))\n}"},{"lang":"php","label":"PHP (HTTP)","source":"<?php\n$request = curl_init(\"https://api.revkeen.com/v2/payment-links\");\ncurl_setopt_array($request, [\n  CURLOPT_CUSTOMREQUEST => \"POST\",\n  CURLOPT_RETURNTRANSFER => true,\n  CURLOPT_TIMEOUT => 10,\n  CURLOPT_HTTPHEADER => [\"x-api-key: \" . getenv(\"REVKEEN_API_KEY\"), \"Content-Type: application/json\"],\n  CURLOPT_POSTFIELDS => '{\\\"product_ids\\\":[\\\"11111111-1111-4111-8111-111111111111\\\"],\\\"line_items\\\":[{\\\"price\\\":\\\"11111111-1111-4111-8111-111111111111\\\",\\\"price_data\\\":{\\\"currency\\\":\\\"USD\\\",\\\"unit_amount\\\":1000,\\\"product_data\\\":{\\\"name\\\":\\\"example\\\",\\\"description\\\":\\\"Example request from API docs\\\",\\\"images\\\":[\\\"https://example.com/success\\\"],\\\"metadata\\\":{\\\"key\\\":\\\"example\\\"}},\\\"product\\\":\\\"11111111-1111-4111-8111-111111111111\\\"},\\\"quantity\\\":1,\\\"merchant_reference\\\":\\\"example-reference\\\"}],\\\"name\\\":\\\"example\\\",\\\"slug\\\":\\\"example\\\"}',\n]);\n$response = curl_exec($request);\nif ($response === false) throw new RuntimeException(curl_error($request));\n$status = curl_getinfo($request, CURLINFO_HTTP_CODE);\ncurl_close($request);\nif ($status >= 400) throw new RuntimeException(\"RevKeen request failed ($status)\");\necho $response;"},{"lang":"python","label":"Python","source":"import os\n\nimport requests\n\nresponse = requests.post(\n    \"https://api.revkeen.com/v2/payment-links\",\n    headers={\"x-api-key\": os.environ[\"REVKEEN_API_KEY\"]},\n    json={\n        \"product_ids\": [\n            \"11111111-1111-4111-8111-111111111111\",\n        ],\n        \"line_items\": [\n            {\n                \"price\": \"11111111-1111-4111-8111-111111111111\",\n                \"price_data\": {\n                    \"currency\": \"USD\",\n                    \"unit_amount\": 1000,\n                    \"product_data\": {\n                        \"name\": \"example\",\n                        \"description\": \"Example request from API docs\",\n                        \"images\": [\n                            \"https://example.com/success\",\n                        ],\n                        \"metadata\": {\n                            \"key\": \"example\",\n                        },\n                    },\n                    \"product\": \"11111111-1111-4111-8111-111111111111\",\n                },\n                \"quantity\": 1,\n                \"merchant_reference\": \"example-reference\",\n            },\n        ],\n        \"name\": \"example\",\n        \"slug\": \"example\",\n    },\n)\n\nresponse.raise_for_status()\nprint(response.json())"},{"lang":"javascript","label":"JavaScript","source":"const credential = process.env.REVKEEN_API_KEY;\nif (!credential) throw new Error(\"Set REVKEEN_API_KEY\");\nconst response = await fetch(\"https://api.revkeen.com/v2/payment-links\", {\n  method: \"POST\",\n  headers: {\n    \"x-api-key\": credential,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n    \"product_ids\": [\n      \"11111111-1111-4111-8111-111111111111\"\n    ],\n    \"line_items\": [\n      {\n        \"price\": \"11111111-1111-4111-8111-111111111111\",\n        \"price_data\": {\n          \"currency\": \"USD\",\n          \"unit_amount\": 1000,\n          \"product_data\": {\n            \"name\": \"example\",\n            \"description\": \"Example request from API docs\",\n            \"images\": [\n              \"https://example.com/success\"\n            ],\n            \"metadata\": {\n              \"key\": \"example\"\n            }\n          },\n          \"product\": \"11111111-1111-4111-8111-111111111111\"\n        },\n        \"quantity\": 1,\n        \"merchant_reference\": \"example-reference\"\n      }\n    ],\n    \"name\": \"example\",\n    \"slug\": \"example\"\n  }),\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"java","label":"Java","source":"import java.net.URI;\nimport java.net.http.HttpClient;\nimport java.net.http.HttpRequest;\nimport java.net.http.HttpResponse;\n\nHttpClient client = HttpClient.newHttpClient();\n\nHttpRequest request = HttpRequest.newBuilder()\n    .uri(URI.create(\"https://api.revkeen.com/v2/payment-links\"))\n    .header(\"x-api-key\", System.getenv(\"REVKEEN_API_KEY\"))\n    .header(\"Content-Type\", \"application/json\")\n    .method(\"POST\", HttpRequest.BodyPublishers.ofString(\"\"\"\n        {\n          \"product_ids\": [\n            \"11111111-1111-4111-8111-111111111111\"\n          ],\n          \"line_items\": [\n            {\n              \"price\": \"11111111-1111-4111-8111-111111111111\",\n              \"price_data\": {\n                \"currency\": \"USD\",\n                \"unit_amount\": 1000,\n                \"product_data\": {\n                  \"name\": \"example\",\n                  \"description\": \"Example request from API docs\",\n                  \"images\": [\n                    \"https://example.com/success\"\n                  ],\n                  \"metadata\": {\n                    \"key\": \"example\"\n                  }\n                },\n                \"product\": \"11111111-1111-4111-8111-111111111111\"\n              },\n              \"quantity\": 1,\n              \"merchant_reference\": \"example-reference\"\n            }\n          ],\n          \"name\": \"example\",\n          \"slug\": \"example\"\n        }\n        \"\"\"))\n    .build();\n\nHttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());\nSystem.out.println(response.body());"},{"lang":"ruby","label":"Ruby","source":"require \"net/http\"\nrequire \"json\"\nrequire \"uri\"\n\nuri = URI(\"https://api.revkeen.com/v2/payment-links\")\nhttp = Net::HTTP.new(uri.host, uri.port)\nhttp.use_ssl = true\n\nrequest = Net::HTTP::Post.new(uri)\nrequest[\"x-api-key\"] = ENV[\"REVKEEN_API_KEY\"]\nrequest[\"Content-Type\"] = \"application/json\"\nrequest.body = {\n  \"product_ids\" => [\n    \"11111111-1111-4111-8111-111111111111\",\n  ],\n  \"line_items\" => [\n    {\n      \"price\" => \"11111111-1111-4111-8111-111111111111\",\n      \"price_data\" => {\n        \"currency\" => \"USD\",\n        \"unit_amount\" => 1000,\n        \"product_data\" => {\n          \"name\" => \"example\",\n          \"description\" => \"Example request from API docs\",\n          \"images\" => [\n            \"https://example.com/success\",\n          ],\n          \"metadata\" => {\n            \"key\" => \"example\",\n          },\n        },\n        \"product\" => \"11111111-1111-4111-8111-111111111111\",\n      },\n      \"quantity\" => 1,\n      \"merchant_reference\" => \"example-reference\",\n    },\n  ],\n  \"name\" => \"example\",\n  \"slug\" => \"example\",\n}.to_json\n\nresponse = http.request(request)\nputs response.body"}],"security":[{"apiKey":["payment_links:write"]},{"oauth2":["payment_links:write"]}]},"get":{"operationId":"payment_links_list","tags":["Payment Links"],"summary":"List payment links","description":"List payment links for the authenticated merchant with pagination and filters\n\n---\n\n**Related endpoints**\n\n- `POST /payment-links` — Create a new payment link\n- `GET /payment-links/{id}` — Get payment link by ID\n- `POST /payment-links/{id}/expire` — Expire a payment link\n- `PATCH /payment-links/{id}/status` — Update payment link status\n- `POST /payment-links/{id}/deactivate` — Deactivate a payment link\n- `POST /payment-links/{id}/activate` — Activate a payment link\n- `POST /payment-links/{id}/archive` — Archive a payment link\n\n**Common errors**\n\n- `400 invalid_request` — malformed payload or failed validation.\n- `401 authentication_error` — missing, invalid, expired, or revoked credential. Codes: `authentication_failed`, `invalid_api_key`, `expired_api_key`, `api_key_revoked`, `session_invalid`, `merchant_required`. Carries a `WWW-Authenticate: Bearer` challenge.\n\n**Pagination**\n\nOffset-based with `limit` (default 25, max 100) and `offset`. The response `pagination` block includes `total` and `hasMore`. See [the pagination guide](/docs/fundamentals/pagination) for SDK auto-paging helpers.","parameters":[{"schema":{"type":"integer","minimum":1,"maximum":100,"default":50},"required":false,"name":"limit","in":"query"},{"schema":{"type":["integer","null"],"minimum":0,"default":0},"required":false,"name":"offset","in":"query"},{"schema":{"type":"string","enum":["active","inactive","archived","expired"],"description":"Filter by link status. active also matches links that have not had a checkout session issued yet."},"required":false,"description":"Filter by link status. active also matches links that have not had a checkout session issued yet.","name":"status","in":"query"},{"schema":{"type":"string","enum":["invoice","subscription","custom"]},"required":false,"name":"mode","in":"query"},{"schema":{"type":"string"},"required":false,"name":"search","in":"query"}],"responses":{"200":{"description":"Payment links retrieved successfully","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"merchant_id":{"type":"string","format":"uuid"},"name":{"type":"string"},"slug":{"type":["string","null"]},"mode":{"type":"string","enum":["invoice","subscription","custom"],"description":"The link's mode. A link created in the dashboard carries a checkout-template value in its stored mode; it is returned here as custom."},"status":{"type":"string","enum":["active","inactive","archived","expired"],"description":"active links accept payments. inactive links are switched off and can be activated again. archived links are permanently disabled. expired links are past their expiry."},"url":{"type":"string","format":"uri","description":"The hosted checkout page for this link. Share this URL with the customer."},"amount_minor":{"type":["integer","null"]},"currency":{"type":"string","minLength":3,"maxLength":3,"description":"Three-letter ISO currency code, taken from the link's products."},"invoice_id":{"type":["string","null"],"format":"uuid"},"subscription_id":{"type":["string","null"],"format":"uuid"},"product_ids":{"type":"array","items":{"type":"string","format":"uuid"}},"line_items":{"type":"array","items":{"type":"object","properties":{"product_id":{"type":"string","format":"uuid"},"price_id":{"type":["string","null"],"format":"uuid"},"quantity":{"type":"integer","minimum":1},"merchant_reference":{"type":["string","null"]},"is_adhoc":{"type":"boolean"},"product_details":{"type":"object","properties":{"name":{"type":"string"},"description":{"type":["string","null"]},"unit_amount":{"type":"integer"},"currency":{"type":"string"},"recurring":{"type":["object","null"],"properties":{"interval":{"type":"string","enum":["day","week","month","year"]},"interval_count":{"type":"integer","minimum":1,"default":1}},"required":["interval"]}},"required":["name","unit_amount","currency"]}},"required":["product_id","quantity","is_adhoc"]}},"success_url":{"type":["string","null"],"format":"uri","description":"Custom URL to redirect after successful payment. Supports placeholders: {CHECKOUT_ID}, {TRANSACTION_ID}, {PAYMENT_ID}, {CUSTOMER_ID}, {AMOUNT}, {CURRENCY}, {PORTAL_URL}, {CHECKOUT_STATUS_TOKEN}. If not set, redirects to RevKeen's default success page."},"cancel_url":{"type":["string","null"],"format":"uri","description":"Custom URL to redirect if customer cancels the checkout. If not set, no cancel redirect is configured."},"redirect_url":{"type":["string","null"],"format":"uri"},"collect_shipping":{"type":"boolean","default":false},"allow_promotion_codes":{"type":"boolean","default":true},"tax_behavior":{"type":"string","enum":["exclusive","inclusive","unspecified"],"default":"unspecified"},"metadata":{"type":["object","null"],"additionalProperties":{}},"expires_at":{"type":["string","null"],"format":"date-time"},"selection_mode":{"type":"string","enum":["bundle","single_select","multi_select"],"default":"bundle","description":"Customer-facing checkout shape. bundle=all products together; single_select=radio picker; multi_select=cart with quantity per line."},"default_product_id":{"type":["string","null"],"format":"uuid","description":"For single_select only — option pre-selected when checkout loads. Must reference a product attached to this payment link."},"highlighted_product_id":{"type":["string","null"],"format":"uuid","description":"For single_select only — option shown with a 'Popular' badge."},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"}},"required":["id","merchant_id","mode","status","url","created_at","updated_at"]}},"pagination":{"type":"object","properties":{"limit":{"type":"number"},"offset":{"type":"number"},"total":{"type":"number"}},"required":["limit","offset","total"]}},"required":["data","pagination"]}}}},"400":{"description":"Validation error in query parameters","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{}},"required":["error"]}}}},"401":{"description":"Unauthorized - missing or invalid authentication","headers":{"WWW-Authenticate":{"description":"RFC 6750 Bearer authentication challenge","schema":{"type":"string"},"example":"Bearer realm=\"revkeen-api\""}}},"429":{"description":"Too Many Requests - Rate limit exceeded","headers":{"Retry-After":{"description":"Whole seconds until the caller may retry","schema":{"type":"string"},"example":"60"},"X-RateLimit-Limit":{"description":"Request quota for the current window","schema":{"type":"string"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window","schema":{"type":"string"}},"X-RateLimit-Reset":{"description":"UNIX timestamp in seconds when the window resets","schema":{"type":"string"}}}}},"x-cli-group":"payment-links","x-cli-name":"list","x-codeSamples":[{"lang":"curl","label":"cURL","source":"curl -X GET \"https://api.revkeen.com/v2/payment-links?limit=50&offset=0\" \\\n  -H \"x-api-key: $REVKEEN_API_KEY\""},{"lang":"typescript","label":"TypeScript (SDK)","source":"// npm install @revkeen/sdk@1.20260822.1541\nimport { RevKeenClient } from \"@revkeen/sdk\";\nconst apiKey = process.env.REVKEEN_API_KEY;\nif (!apiKey) throw new Error(\"Set REVKEEN_API_KEY\");\nconst client = new RevKeenClient({ apiKey, baseUrl: \"https://api.revkeen.com/v2\" });\nconst result = await client.paymentLinks.list({\n  \"limit\": 50,\n  \"offset\": 0\n});\nconsole.log(result);"},{"lang":"go","label":"Go (HTTP)","source":"package main\nimport (\"fmt\"; \"io\"; \"net/http\"; \"os\"; \"strings\"; \"time\")\nfunc main() {\n  req, err := http.NewRequest(\"GET\", \"https://api.revkeen.com/v2/payment-links?limit=50&offset=0\", strings.NewReader(\"\"))\n  if err != nil { panic(err) }\n  req.Header.Set(\"x-api-key\", os.Getenv(\"REVKEEN_API_KEY\"))\n  req.Header.Set(\"Content-Type\", \"application/json\")\n  client := &http.Client{Timeout: 10 * time.Second}\n  response, err := client.Do(req)\n  if err != nil { panic(err) }\n  defer response.Body.Close()\n  if response.StatusCode >= 400 { panic(fmt.Sprintf(\"RevKeen request failed (%d)\", response.StatusCode)) }\n  data, err := io.ReadAll(response.Body)\n  if err != nil { panic(err) }\n  fmt.Println(string(data))\n}"},{"lang":"php","label":"PHP (HTTP)","source":"<?php\n$request = curl_init(\"https://api.revkeen.com/v2/payment-links?limit=50&offset=0\");\ncurl_setopt_array($request, [\n  CURLOPT_CUSTOMREQUEST => \"GET\",\n  CURLOPT_RETURNTRANSFER => true,\n  CURLOPT_TIMEOUT => 10,\n  CURLOPT_HTTPHEADER => [\"x-api-key: \" . getenv(\"REVKEEN_API_KEY\"), \"Content-Type: application/json\"],\n]);\n$response = curl_exec($request);\nif ($response === false) throw new RuntimeException(curl_error($request));\n$status = curl_getinfo($request, CURLINFO_HTTP_CODE);\ncurl_close($request);\nif ($status >= 400) throw new RuntimeException(\"RevKeen request failed ($status)\");\necho $response;"},{"lang":"python","label":"Python","source":"import os\n\nimport requests\n\nresponse = requests.get(\n    \"https://api.revkeen.com/v2/payment-links\",\n    headers={\"x-api-key\": os.environ[\"REVKEEN_API_KEY\"]},\n    params={\n        \"limit\": 50,\n        \"offset\": 0,\n    },\n)\n\nresponse.raise_for_status()\nprint(response.json())"},{"lang":"javascript","label":"JavaScript","source":"const credential = process.env.REVKEEN_API_KEY;\nif (!credential) throw new Error(\"Set REVKEEN_API_KEY\");\nconst response = await fetch(\"https://api.revkeen.com/v2/payment-links?limit=50&offset=0\", {\n  method: \"GET\",\n  headers: {\n    \"x-api-key\": credential,\n  },\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"java","label":"Java","source":"import java.net.URI;\nimport java.net.http.HttpClient;\nimport java.net.http.HttpRequest;\nimport java.net.http.HttpResponse;\n\nHttpClient client = HttpClient.newHttpClient();\n\nHttpRequest request = HttpRequest.newBuilder()\n    .uri(URI.create(\"https://api.revkeen.com/v2/payment-links?limit=50&offset=0\"))\n    .header(\"x-api-key\", System.getenv(\"REVKEEN_API_KEY\"))\n    .GET()\n    .build();\n\nHttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());\nSystem.out.println(response.body());"},{"lang":"ruby","label":"Ruby","source":"require \"net/http\"\nrequire \"json\"\nrequire \"uri\"\n\nuri = URI(\"https://api.revkeen.com/v2/payment-links?limit=50&offset=0\")\nhttp = Net::HTTP.new(uri.host, uri.port)\nhttp.use_ssl = true\n\nrequest = Net::HTTP::Get.new(uri)\nrequest[\"x-api-key\"] = ENV[\"REVKEEN_API_KEY\"]\n\nresponse = http.request(request)\nputs response.body"}],"security":[{"apiKey":["payment_links:read"]},{"oauth2":["payment_links:read"]}]}},"/payment-links/{id}":{"get":{"operationId":"payment_links_get","tags":["Payment Links"],"summary":"Get payment link by ID","description":"Retrieve a payment link by its UUID.\n\n---\n\n**Related endpoints**\n\n- `POST /payment-links` — Create a new payment link\n- `GET /payment-links` — List payment links\n- `POST /payment-links/{id}/expire` — Expire a payment link\n- `PATCH /payment-links/{id}/status` — Update payment link status\n- `POST /payment-links/{id}/deactivate` — Deactivate a payment link\n- `POST /payment-links/{id}/activate` — Activate a payment link\n- `POST /payment-links/{id}/archive` — Archive a payment link\n\n**Common errors**\n\n- `400 invalid_request` — malformed payload or failed validation.\n- `401 authentication_error` — missing, invalid, expired, or revoked credential. Codes: `authentication_failed`, `invalid_api_key`, `expired_api_key`, `api_key_revoked`, `session_invalid`, `merchant_required`. Carries a `WWW-Authenticate: Bearer` challenge.\n- `404 resource_missing` — the referenced resource does not exist or is not visible to your key.","parameters":[{"schema":{"type":"string","format":"uuid","description":"Payment link UUID.","example":"e3e70682-c209-1cac-a29f-6fbed82c07cd"},"required":true,"description":"Payment link UUID.","name":"id","in":"path"}],"responses":{"200":{"description":"Payment link retrieved successfully","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"merchant_id":{"type":"string","format":"uuid"},"name":{"type":"string"},"slug":{"type":["string","null"]},"mode":{"type":"string","enum":["invoice","subscription","custom"],"description":"The link's mode. A link created in the dashboard carries a checkout-template value in its stored mode; it is returned here as custom."},"status":{"type":"string","enum":["active","inactive","archived","expired"],"description":"active links accept payments. inactive links are switched off and can be activated again. archived links are permanently disabled. expired links are past their expiry."},"url":{"type":"string","format":"uri","description":"The hosted checkout page for this link. Share this URL with the customer."},"amount_minor":{"type":["integer","null"]},"currency":{"type":"string","minLength":3,"maxLength":3,"description":"Three-letter ISO currency code, taken from the link's products."},"invoice_id":{"type":["string","null"],"format":"uuid"},"subscription_id":{"type":["string","null"],"format":"uuid"},"product_ids":{"type":"array","items":{"type":"string","format":"uuid"}},"line_items":{"type":"array","items":{"type":"object","properties":{"product_id":{"type":"string","format":"uuid"},"price_id":{"type":["string","null"],"format":"uuid"},"quantity":{"type":"integer","minimum":1},"merchant_reference":{"type":["string","null"]},"is_adhoc":{"type":"boolean"},"product_details":{"type":"object","properties":{"name":{"type":"string"},"description":{"type":["string","null"]},"unit_amount":{"type":"integer"},"currency":{"type":"string"},"recurring":{"type":["object","null"],"properties":{"interval":{"type":"string","enum":["day","week","month","year"]},"interval_count":{"type":"integer","minimum":1,"default":1}},"required":["interval"]}},"required":["name","unit_amount","currency"]}},"required":["product_id","quantity","is_adhoc"]}},"success_url":{"type":["string","null"],"format":"uri","description":"Custom URL to redirect after successful payment. Supports placeholders: {CHECKOUT_ID}, {TRANSACTION_ID}, {PAYMENT_ID}, {CUSTOMER_ID}, {AMOUNT}, {CURRENCY}, {PORTAL_URL}, {CHECKOUT_STATUS_TOKEN}. If not set, redirects to RevKeen's default success page."},"cancel_url":{"type":["string","null"],"format":"uri","description":"Custom URL to redirect if customer cancels the checkout. If not set, no cancel redirect is configured."},"redirect_url":{"type":["string","null"],"format":"uri"},"collect_shipping":{"type":"boolean","default":false},"allow_promotion_codes":{"type":"boolean","default":true},"tax_behavior":{"type":"string","enum":["exclusive","inclusive","unspecified"],"default":"unspecified"},"metadata":{"type":["object","null"],"additionalProperties":{}},"expires_at":{"type":["string","null"],"format":"date-time"},"selection_mode":{"type":"string","enum":["bundle","single_select","multi_select"],"default":"bundle","description":"Customer-facing checkout shape. bundle=all products together; single_select=radio picker; multi_select=cart with quantity per line."},"default_product_id":{"type":["string","null"],"format":"uuid","description":"For single_select only — option pre-selected when checkout loads. Must reference a product attached to this payment link."},"highlighted_product_id":{"type":["string","null"],"format":"uuid","description":"For single_select only — option shown with a 'Popular' badge."},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"}},"required":["id","merchant_id","mode","status","url","created_at","updated_at"]}}}},"400":{"description":"`id` is not a UUID","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaymentLinkRetrieveValidationError"}}}},"401":{"description":"Unauthorized - missing or invalid authentication","headers":{"WWW-Authenticate":{"description":"RFC 6750 Bearer authentication challenge","schema":{"type":"string"},"example":"Bearer realm=\"revkeen-api\""}}},"404":{"description":"Payment link not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{}},"required":["error"]}}}},"429":{"description":"Too Many Requests - Rate limit exceeded","headers":{"Retry-After":{"description":"Whole seconds until the caller may retry","schema":{"type":"string"},"example":"60"},"X-RateLimit-Limit":{"description":"Request quota for the current window","schema":{"type":"string"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window","schema":{"type":"string"}},"X-RateLimit-Reset":{"description":"UNIX timestamp in seconds when the window resets","schema":{"type":"string"}}}}},"x-cli-group":"payment-links","x-cli-name":"get","x-codeSamples":[{"lang":"curl","label":"cURL","source":"curl -X GET \"https://api.revkeen.com/v2/payment-links/11111111-1111-4111-8111-111111111111\" \\\n  -H \"x-api-key: $REVKEEN_API_KEY\""},{"lang":"typescript","label":"TypeScript (SDK)","source":"// npm install @revkeen/sdk@1.20260822.1541\nimport { RevKeenClient } from \"@revkeen/sdk\";\nconst apiKey = process.env.REVKEEN_API_KEY;\nif (!apiKey) throw new Error(\"Set REVKEEN_API_KEY\");\nconst client = new RevKeenClient({ apiKey, baseUrl: \"https://api.revkeen.com/v2\" });\nconst result = await client.paymentLinks.get(\"11111111-1111-4111-8111-111111111111\");\nconsole.log(result);"},{"lang":"go","label":"Go (HTTP)","source":"package main\nimport (\"fmt\"; \"io\"; \"net/http\"; \"os\"; \"strings\"; \"time\")\nfunc main() {\n  req, err := http.NewRequest(\"GET\", \"https://api.revkeen.com/v2/payment-links/11111111-1111-4111-8111-111111111111\", strings.NewReader(\"\"))\n  if err != nil { panic(err) }\n  req.Header.Set(\"x-api-key\", os.Getenv(\"REVKEEN_API_KEY\"))\n  req.Header.Set(\"Content-Type\", \"application/json\")\n  client := &http.Client{Timeout: 10 * time.Second}\n  response, err := client.Do(req)\n  if err != nil { panic(err) }\n  defer response.Body.Close()\n  if response.StatusCode >= 400 { panic(fmt.Sprintf(\"RevKeen request failed (%d)\", response.StatusCode)) }\n  data, err := io.ReadAll(response.Body)\n  if err != nil { panic(err) }\n  fmt.Println(string(data))\n}"},{"lang":"php","label":"PHP (HTTP)","source":"<?php\n$request = curl_init(\"https://api.revkeen.com/v2/payment-links/11111111-1111-4111-8111-111111111111\");\ncurl_setopt_array($request, [\n  CURLOPT_CUSTOMREQUEST => \"GET\",\n  CURLOPT_RETURNTRANSFER => true,\n  CURLOPT_TIMEOUT => 10,\n  CURLOPT_HTTPHEADER => [\"x-api-key: \" . getenv(\"REVKEEN_API_KEY\"), \"Content-Type: application/json\"],\n]);\n$response = curl_exec($request);\nif ($response === false) throw new RuntimeException(curl_error($request));\n$status = curl_getinfo($request, CURLINFO_HTTP_CODE);\ncurl_close($request);\nif ($status >= 400) throw new RuntimeException(\"RevKeen request failed ($status)\");\necho $response;"},{"lang":"python","label":"Python","source":"import os\n\nimport requests\n\nresponse = requests.get(\n    \"https://api.revkeen.com/v2/payment-links/11111111-1111-4111-8111-111111111111\",\n    headers={\"x-api-key\": os.environ[\"REVKEEN_API_KEY\"]},\n)\n\nresponse.raise_for_status()\nprint(response.json())"},{"lang":"javascript","label":"JavaScript","source":"const credential = process.env.REVKEEN_API_KEY;\nif (!credential) throw new Error(\"Set REVKEEN_API_KEY\");\nconst response = await fetch(\"https://api.revkeen.com/v2/payment-links/11111111-1111-4111-8111-111111111111\", {\n  method: \"GET\",\n  headers: {\n    \"x-api-key\": credential,\n  },\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"java","label":"Java","source":"import java.net.URI;\nimport java.net.http.HttpClient;\nimport java.net.http.HttpRequest;\nimport java.net.http.HttpResponse;\n\nHttpClient client = HttpClient.newHttpClient();\n\nHttpRequest request = HttpRequest.newBuilder()\n    .uri(URI.create(\"https://api.revkeen.com/v2/payment-links/11111111-1111-4111-8111-111111111111\"))\n    .header(\"x-api-key\", System.getenv(\"REVKEEN_API_KEY\"))\n    .GET()\n    .build();\n\nHttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());\nSystem.out.println(response.body());"},{"lang":"ruby","label":"Ruby","source":"require \"net/http\"\nrequire \"json\"\nrequire \"uri\"\n\nuri = URI(\"https://api.revkeen.com/v2/payment-links/11111111-1111-4111-8111-111111111111\")\nhttp = Net::HTTP.new(uri.host, uri.port)\nhttp.use_ssl = true\n\nrequest = Net::HTTP::Get.new(uri)\nrequest[\"x-api-key\"] = ENV[\"REVKEEN_API_KEY\"]\n\nresponse = http.request(request)\nputs response.body"}],"security":[{"apiKey":["payment_links:read"]},{"oauth2":["payment_links:read"]}]}},"/payment-links/{id}/expire":{"post":{"operationId":"payment_links_expire","tags":["Payment Links"],"summary":"Expire a payment link","description":"Mark a payment link as expired, preventing further use. This is the way to expire a link: the status endpoint does not accept `expired`.\n\n---\n\n**Related endpoints**\n\n- `POST /payment-links` — Create a new payment link\n- `GET /payment-links` — List payment links\n- `GET /payment-links/{id}` — Get payment link by ID\n- `PATCH /payment-links/{id}/status` — Update payment link status\n- `POST /payment-links/{id}/deactivate` — Deactivate a payment link\n- `POST /payment-links/{id}/activate` — Activate a payment link\n- `POST /payment-links/{id}/archive` — Archive a payment link\n\n**Common errors**\n\n- `400 invalid_request` — malformed payload or failed validation.\n- `401 authentication_error` — missing, invalid, expired, or revoked credential. Codes: `authentication_failed`, `invalid_api_key`, `expired_api_key`, `api_key_revoked`, `session_invalid`, `merchant_required`. Carries a `WWW-Authenticate: Bearer` challenge.\n- `404 resource_missing` — the referenced resource does not exist or is not visible to your key.\n\n**Idempotency**\n\nPass an `Idempotency-Key` header (UUID v4 recommended) to make retries safe. Keys are valid for 24 hours; see [the idempotency guide](/docs/fundamentals/idempotency).","parameters":[{"schema":{"type":"string","format":"uuid","description":"Payment link UUID.","example":"e3e70682-c209-1cac-a29f-6fbed82c07cd"},"required":true,"description":"Payment link UUID.","name":"id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"reason":{"type":"string"}}}}}},"responses":{"200":{"description":"Payment link expired successfully","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"merchant_id":{"type":"string","format":"uuid"},"name":{"type":"string"},"slug":{"type":["string","null"]},"mode":{"type":"string","enum":["invoice","subscription","custom"],"description":"The link's mode. A link created in the dashboard carries a checkout-template value in its stored mode; it is returned here as custom."},"status":{"type":"string","enum":["active","inactive","archived","expired"],"description":"active links accept payments. inactive links are switched off and can be activated again. archived links are permanently disabled. expired links are past their expiry."},"url":{"type":"string","format":"uri","description":"The hosted checkout page for this link. Share this URL with the customer."},"amount_minor":{"type":["integer","null"]},"currency":{"type":"string","minLength":3,"maxLength":3,"description":"Three-letter ISO currency code, taken from the link's products."},"invoice_id":{"type":["string","null"],"format":"uuid"},"subscription_id":{"type":["string","null"],"format":"uuid"},"product_ids":{"type":"array","items":{"type":"string","format":"uuid"}},"line_items":{"type":"array","items":{"type":"object","properties":{"product_id":{"type":"string","format":"uuid"},"price_id":{"type":["string","null"],"format":"uuid"},"quantity":{"type":"integer","minimum":1},"merchant_reference":{"type":["string","null"]},"is_adhoc":{"type":"boolean"},"product_details":{"type":"object","properties":{"name":{"type":"string"},"description":{"type":["string","null"]},"unit_amount":{"type":"integer"},"currency":{"type":"string"},"recurring":{"type":["object","null"],"properties":{"interval":{"type":"string","enum":["day","week","month","year"]},"interval_count":{"type":"integer","minimum":1,"default":1}},"required":["interval"]}},"required":["name","unit_amount","currency"]}},"required":["product_id","quantity","is_adhoc"]}},"success_url":{"type":["string","null"],"format":"uri","description":"Custom URL to redirect after successful payment. Supports placeholders: {CHECKOUT_ID}, {TRANSACTION_ID}, {PAYMENT_ID}, {CUSTOMER_ID}, {AMOUNT}, {CURRENCY}, {PORTAL_URL}, {CHECKOUT_STATUS_TOKEN}. If not set, redirects to RevKeen's default success page."},"cancel_url":{"type":["string","null"],"format":"uri","description":"Custom URL to redirect if customer cancels the checkout. If not set, no cancel redirect is configured."},"redirect_url":{"type":["string","null"],"format":"uri"},"collect_shipping":{"type":"boolean","default":false},"allow_promotion_codes":{"type":"boolean","default":true},"tax_behavior":{"type":"string","enum":["exclusive","inclusive","unspecified"],"default":"unspecified"},"metadata":{"type":["object","null"],"additionalProperties":{}},"expires_at":{"type":["string","null"],"format":"date-time"},"selection_mode":{"type":"string","enum":["bundle","single_select","multi_select"],"default":"bundle","description":"Customer-facing checkout shape. bundle=all products together; single_select=radio picker; multi_select=cart with quantity per line."},"default_product_id":{"type":["string","null"],"format":"uuid","description":"For single_select only — option pre-selected when checkout loads. Must reference a product attached to this payment link."},"highlighted_product_id":{"type":["string","null"],"format":"uuid","description":"For single_select only — option shown with a 'Popular' badge."},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"}},"required":["id","merchant_id","mode","status","url","created_at","updated_at"]}}}},"400":{"description":"Validation error or payment link already expired","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{}},"required":["error"]}}}},"401":{"description":"Unauthorized - missing or invalid authentication","headers":{"WWW-Authenticate":{"description":"RFC 6750 Bearer authentication challenge","schema":{"type":"string"},"example":"Bearer realm=\"revkeen-api\""}}},"404":{"description":"Payment link not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{}},"required":["error"]}}}},"429":{"description":"Too Many Requests - Rate limit exceeded","headers":{"Retry-After":{"description":"Whole seconds until the caller may retry","schema":{"type":"string"},"example":"60"},"X-RateLimit-Limit":{"description":"Request quota for the current window","schema":{"type":"string"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window","schema":{"type":"string"}},"X-RateLimit-Reset":{"description":"UNIX timestamp in seconds when the window resets","schema":{"type":"string"}}}}},"x-cli-group":"payment-links","x-cli-name":"expire","x-codeSamples":[{"lang":"curl","label":"cURL","source":"curl -X POST \"https://api.revkeen.com/v2/payment-links/11111111-1111-4111-8111-111111111111/expire\" \\\n  -H \"x-api-key: $REVKEEN_API_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"reason\": \"example\"\n  }'"},{"lang":"typescript","label":"TypeScript (SDK)","source":"// npm install @revkeen/sdk@1.20260822.1541\nimport { RevKeenClient } from \"@revkeen/sdk\";\nconst apiKey = process.env.REVKEEN_API_KEY;\nif (!apiKey) throw new Error(\"Set REVKEEN_API_KEY\");\nconst client = new RevKeenClient({ apiKey, baseUrl: \"https://api.revkeen.com/v2\" });\nconst result = await client.paymentLinks.expire(\"11111111-1111-4111-8111-111111111111\", {\n  \"reason\": \"example\"\n});\nconsole.log(result);"},{"lang":"go","label":"Go (HTTP)","source":"package main\nimport (\"fmt\"; \"io\"; \"net/http\"; \"os\"; \"strings\"; \"time\")\nfunc main() {\n  req, err := http.NewRequest(\"POST\", \"https://api.revkeen.com/v2/payment-links/11111111-1111-4111-8111-111111111111/expire\", strings.NewReader(\"{\\\"reason\\\":\\\"example\\\"}\"))\n  if err != nil { panic(err) }\n  req.Header.Set(\"x-api-key\", os.Getenv(\"REVKEEN_API_KEY\"))\n  req.Header.Set(\"Content-Type\", \"application/json\")\n  client := &http.Client{Timeout: 10 * time.Second}\n  response, err := client.Do(req)\n  if err != nil { panic(err) }\n  defer response.Body.Close()\n  if response.StatusCode >= 400 { panic(fmt.Sprintf(\"RevKeen request failed (%d)\", response.StatusCode)) }\n  data, err := io.ReadAll(response.Body)\n  if err != nil { panic(err) }\n  fmt.Println(string(data))\n}"},{"lang":"php","label":"PHP (HTTP)","source":"<?php\n$request = curl_init(\"https://api.revkeen.com/v2/payment-links/11111111-1111-4111-8111-111111111111/expire\");\ncurl_setopt_array($request, [\n  CURLOPT_CUSTOMREQUEST => \"POST\",\n  CURLOPT_RETURNTRANSFER => true,\n  CURLOPT_TIMEOUT => 10,\n  CURLOPT_HTTPHEADER => [\"x-api-key: \" . getenv(\"REVKEEN_API_KEY\"), \"Content-Type: application/json\"],\n  CURLOPT_POSTFIELDS => '{\\\"reason\\\":\\\"example\\\"}',\n]);\n$response = curl_exec($request);\nif ($response === false) throw new RuntimeException(curl_error($request));\n$status = curl_getinfo($request, CURLINFO_HTTP_CODE);\ncurl_close($request);\nif ($status >= 400) throw new RuntimeException(\"RevKeen request failed ($status)\");\necho $response;"},{"lang":"python","label":"Python","source":"import os\n\nimport requests\n\nresponse = requests.post(\n    \"https://api.revkeen.com/v2/payment-links/11111111-1111-4111-8111-111111111111/expire\",\n    headers={\"x-api-key\": os.environ[\"REVKEEN_API_KEY\"]},\n    json={\n        \"reason\": \"example\",\n    },\n)\n\nresponse.raise_for_status()\nprint(response.json())"},{"lang":"javascript","label":"JavaScript","source":"const credential = process.env.REVKEEN_API_KEY;\nif (!credential) throw new Error(\"Set REVKEEN_API_KEY\");\nconst response = await fetch(\"https://api.revkeen.com/v2/payment-links/11111111-1111-4111-8111-111111111111/expire\", {\n  method: \"POST\",\n  headers: {\n    \"x-api-key\": credential,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n    \"reason\": \"example\"\n  }),\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"java","label":"Java","source":"import java.net.URI;\nimport java.net.http.HttpClient;\nimport java.net.http.HttpRequest;\nimport java.net.http.HttpResponse;\n\nHttpClient client = HttpClient.newHttpClient();\n\nHttpRequest request = HttpRequest.newBuilder()\n    .uri(URI.create(\"https://api.revkeen.com/v2/payment-links/11111111-1111-4111-8111-111111111111/expire\"))\n    .header(\"x-api-key\", System.getenv(\"REVKEEN_API_KEY\"))\n    .header(\"Content-Type\", \"application/json\")\n    .method(\"POST\", HttpRequest.BodyPublishers.ofString(\"\"\"\n        {\n          \"reason\": \"example\"\n        }\n        \"\"\"))\n    .build();\n\nHttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());\nSystem.out.println(response.body());"},{"lang":"ruby","label":"Ruby","source":"require \"net/http\"\nrequire \"json\"\nrequire \"uri\"\n\nuri = URI(\"https://api.revkeen.com/v2/payment-links/11111111-1111-4111-8111-111111111111/expire\")\nhttp = Net::HTTP.new(uri.host, uri.port)\nhttp.use_ssl = true\n\nrequest = Net::HTTP::Post.new(uri)\nrequest[\"x-api-key\"] = ENV[\"REVKEEN_API_KEY\"]\nrequest[\"Content-Type\"] = \"application/json\"\nrequest.body = {\n  \"reason\" => \"example\",\n}.to_json\n\nresponse = http.request(request)\nputs response.body"}],"security":[{"apiKey":["payment_links:write"]},{"oauth2":["payment_links:write"]}]}},"/payment-links/{id}/status":{"patch":{"operationId":"payment_links_update","tags":["Payment Links"],"summary":"Update payment link status","description":"Change the status of a payment link. Active links accept payments, inactive links are temporarily disabled, and archived links are permanently disabled and cannot be reactivated.\n\n---\n\n**Related endpoints**\n\n- `POST /payment-links` — Create a new payment link\n- `GET /payment-links` — List payment links\n- `GET /payment-links/{id}` — Get payment link by ID\n- `POST /payment-links/{id}/expire` — Expire a payment link\n- `POST /payment-links/{id}/deactivate` — Deactivate a payment link\n- `POST /payment-links/{id}/activate` — Activate a payment link\n- `POST /payment-links/{id}/archive` — Archive a payment link\n\n**Common errors**\n\n- `400 invalid_request` — malformed payload or failed validation.\n- `401 authentication_error` — missing, invalid, expired, or revoked credential. Codes: `authentication_failed`, `invalid_api_key`, `expired_api_key`, `api_key_revoked`, `session_invalid`, `merchant_required`. Carries a `WWW-Authenticate: Bearer` challenge.\n- `404 resource_missing` — the referenced resource does not exist or is not visible to your key.\n\n**Idempotency**\n\nPass an `Idempotency-Key` header (UUID v4 recommended) to make retries safe. Keys are valid for 24 hours; see [the idempotency guide](/docs/fundamentals/idempotency).","parameters":[{"schema":{"type":"string","format":"uuid","description":"Payment link UUID.","example":"e3e70682-c209-1cac-a29f-6fbed82c07cd"},"required":true,"description":"Payment link UUID.","name":"id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","enum":["active","inactive","archived"],"description":"New status for the payment link"}},"required":["status"]}}}},"responses":{"200":{"description":"Status updated successfully","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"status":{"type":"string","enum":["active","inactive","archived"]},"status_changed_at":{"type":"string","format":"date-time"},"is_active":{"type":"boolean"},"updated_at":{"type":"string","format":"date-time"}},"required":["id","status","status_changed_at","is_active","updated_at"]}}}},"400":{"description":"Invalid status transition (e.g., trying to reactivate archived link)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{}},"required":["error"]}}}},"401":{"description":"Unauthorized - missing or invalid authentication","headers":{"WWW-Authenticate":{"description":"RFC 6750 Bearer authentication challenge","schema":{"type":"string"},"example":"Bearer realm=\"revkeen-api\""}}},"404":{"description":"Payment link not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{}},"required":["error"]}}}},"429":{"description":"Too Many Requests - Rate limit exceeded","headers":{"Retry-After":{"description":"Whole seconds until the caller may retry","schema":{"type":"string"},"example":"60"},"X-RateLimit-Limit":{"description":"Request quota for the current window","schema":{"type":"string"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window","schema":{"type":"string"}},"X-RateLimit-Reset":{"description":"UNIX timestamp in seconds when the window resets","schema":{"type":"string"}}}}},"x-cli-group":"payment-links","x-cli-name":"update","x-codeSamples":[{"lang":"curl","label":"cURL","source":"curl -X PATCH \"https://api.revkeen.com/v2/payment-links/11111111-1111-4111-8111-111111111111/status\" \\\n  -H \"x-api-key: $REVKEEN_API_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"status\": \"active\"\n  }'"},{"lang":"typescript","label":"TypeScript (SDK)","source":"// npm install @revkeen/sdk@1.20260822.1541\nimport { RevKeenClient } from \"@revkeen/sdk\";\nconst apiKey = process.env.REVKEEN_API_KEY;\nif (!apiKey) throw new Error(\"Set REVKEEN_API_KEY\");\nconst client = new RevKeenClient({ apiKey, baseUrl: \"https://api.revkeen.com/v2\" });\nconst result = await client.paymentLinks.update(\"11111111-1111-4111-8111-111111111111\", {\n  \"status\": \"active\"\n});\nconsole.log(result);"},{"lang":"go","label":"Go (HTTP)","source":"package main\nimport (\"fmt\"; \"io\"; \"net/http\"; \"os\"; \"strings\"; \"time\")\nfunc main() {\n  req, err := http.NewRequest(\"PATCH\", \"https://api.revkeen.com/v2/payment-links/11111111-1111-4111-8111-111111111111/status\", strings.NewReader(\"{\\\"status\\\":\\\"active\\\"}\"))\n  if err != nil { panic(err) }\n  req.Header.Set(\"x-api-key\", os.Getenv(\"REVKEEN_API_KEY\"))\n  req.Header.Set(\"Content-Type\", \"application/json\")\n  client := &http.Client{Timeout: 10 * time.Second}\n  response, err := client.Do(req)\n  if err != nil { panic(err) }\n  defer response.Body.Close()\n  if response.StatusCode >= 400 { panic(fmt.Sprintf(\"RevKeen request failed (%d)\", response.StatusCode)) }\n  data, err := io.ReadAll(response.Body)\n  if err != nil { panic(err) }\n  fmt.Println(string(data))\n}"},{"lang":"php","label":"PHP (HTTP)","source":"<?php\n$request = curl_init(\"https://api.revkeen.com/v2/payment-links/11111111-1111-4111-8111-111111111111/status\");\ncurl_setopt_array($request, [\n  CURLOPT_CUSTOMREQUEST => \"PATCH\",\n  CURLOPT_RETURNTRANSFER => true,\n  CURLOPT_TIMEOUT => 10,\n  CURLOPT_HTTPHEADER => [\"x-api-key: \" . getenv(\"REVKEEN_API_KEY\"), \"Content-Type: application/json\"],\n  CURLOPT_POSTFIELDS => '{\\\"status\\\":\\\"active\\\"}',\n]);\n$response = curl_exec($request);\nif ($response === false) throw new RuntimeException(curl_error($request));\n$status = curl_getinfo($request, CURLINFO_HTTP_CODE);\ncurl_close($request);\nif ($status >= 400) throw new RuntimeException(\"RevKeen request failed ($status)\");\necho $response;"},{"lang":"python","label":"Python","source":"import os\n\nimport requests\n\nresponse = requests.patch(\n    \"https://api.revkeen.com/v2/payment-links/11111111-1111-4111-8111-111111111111/status\",\n    headers={\"x-api-key\": os.environ[\"REVKEEN_API_KEY\"]},\n    json={\n        \"status\": \"active\",\n    },\n)\n\nresponse.raise_for_status()\nprint(response.json())"},{"lang":"javascript","label":"JavaScript","source":"const credential = process.env.REVKEEN_API_KEY;\nif (!credential) throw new Error(\"Set REVKEEN_API_KEY\");\nconst response = await fetch(\"https://api.revkeen.com/v2/payment-links/11111111-1111-4111-8111-111111111111/status\", {\n  method: \"PATCH\",\n  headers: {\n    \"x-api-key\": credential,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n    \"status\": \"active\"\n  }),\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"java","label":"Java","source":"import java.net.URI;\nimport java.net.http.HttpClient;\nimport java.net.http.HttpRequest;\nimport java.net.http.HttpResponse;\n\nHttpClient client = HttpClient.newHttpClient();\n\nHttpRequest request = HttpRequest.newBuilder()\n    .uri(URI.create(\"https://api.revkeen.com/v2/payment-links/11111111-1111-4111-8111-111111111111/status\"))\n    .header(\"x-api-key\", System.getenv(\"REVKEEN_API_KEY\"))\n    .header(\"Content-Type\", \"application/json\")\n    .method(\"PATCH\", HttpRequest.BodyPublishers.ofString(\"\"\"\n        {\n          \"status\": \"active\"\n        }\n        \"\"\"))\n    .build();\n\nHttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());\nSystem.out.println(response.body());"},{"lang":"ruby","label":"Ruby","source":"require \"net/http\"\nrequire \"json\"\nrequire \"uri\"\n\nuri = URI(\"https://api.revkeen.com/v2/payment-links/11111111-1111-4111-8111-111111111111/status\")\nhttp = Net::HTTP.new(uri.host, uri.port)\nhttp.use_ssl = true\n\nrequest = Net::HTTP::Patch.new(uri)\nrequest[\"x-api-key\"] = ENV[\"REVKEEN_API_KEY\"]\nrequest[\"Content-Type\"] = \"application/json\"\nrequest.body = {\n  \"status\" => \"active\",\n}.to_json\n\nresponse = http.request(request)\nputs response.body"}],"security":[{"apiKey":["payment_links:write"]},{"oauth2":["payment_links:write"]}]}},"/payment-links/{id}/deactivate":{"post":{"operationId":"payment_links_deactivate","tags":["Payment Links"],"summary":"Deactivate a payment link","description":"Temporarily disable a payment link. It can be reactivated later. Equivalent to `PATCH /v2/payment-links/{id}/status` with `status: \"inactive\"`.\n\n---\n\n**Related endpoints**\n\n- `POST /payment-links` — Create a new payment link\n- `GET /payment-links` — List payment links\n- `GET /payment-links/{id}` — Get payment link by ID\n- `POST /payment-links/{id}/expire` — Expire a payment link\n- `PATCH /payment-links/{id}/status` — Update payment link status\n- `POST /payment-links/{id}/activate` — Activate a payment link\n- `POST /payment-links/{id}/archive` — Archive a payment link\n\n**Common errors**\n\n- `400 invalid_request` — malformed payload or failed validation.\n- `401 authentication_error` — missing, invalid, expired, or revoked credential. Codes: `authentication_failed`, `invalid_api_key`, `expired_api_key`, `api_key_revoked`, `session_invalid`, `merchant_required`. Carries a `WWW-Authenticate: Bearer` challenge.\n- `404 resource_missing` — the referenced resource does not exist or is not visible to your key.\n\n**Idempotency**\n\nPass an `Idempotency-Key` header (UUID v4 recommended) to make retries safe. Keys are valid for 24 hours; see [the idempotency guide](/docs/fundamentals/idempotency).","parameters":[{"schema":{"type":"string","format":"uuid","description":"Payment link UUID.","example":"e3e70682-c209-1cac-a29f-6fbed82c07cd"},"required":true,"description":"Payment link UUID.","name":"id","in":"path"}],"responses":{"200":{"description":"Payment link deactivated","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"status":{"type":"string","enum":["active","inactive","archived"]},"status_changed_at":{"type":"string","format":"date-time"},"is_active":{"type":"boolean"},"updated_at":{"type":"string","format":"date-time"}},"required":["id","status","status_changed_at","is_active","updated_at"]}}}},"400":{"description":"Link is already archived and cannot be modified","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{}},"required":["error"]}}}},"401":{"description":"Unauthorized","headers":{"WWW-Authenticate":{"description":"RFC 6750 Bearer authentication challenge","schema":{"type":"string"},"example":"Bearer realm=\"revkeen-api\""}}},"404":{"description":"Payment link not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{}},"required":["error"]}}}},"429":{"description":"Too Many Requests - Rate limit exceeded","headers":{"Retry-After":{"description":"Whole seconds until the caller may retry","schema":{"type":"string"},"example":"60"},"X-RateLimit-Limit":{"description":"Request quota for the current window","schema":{"type":"string"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window","schema":{"type":"string"}},"X-RateLimit-Reset":{"description":"UNIX timestamp in seconds when the window resets","schema":{"type":"string"}}}}},"x-cli-group":"payment-links","x-cli-name":"deactivate","x-codeSamples":[{"lang":"curl","label":"cURL","source":"curl -X POST \"https://api.revkeen.com/v2/payment-links/11111111-1111-4111-8111-111111111111/deactivate\" \\\n  -H \"x-api-key: $REVKEEN_API_KEY\""},{"lang":"typescript","label":"TypeScript (SDK)","source":"// npm install @revkeen/sdk@1.20260822.1541\nimport { RevKeenClient } from \"@revkeen/sdk\";\nconst apiKey = process.env.REVKEEN_API_KEY;\nif (!apiKey) throw new Error(\"Set REVKEEN_API_KEY\");\nconst client = new RevKeenClient({ apiKey, baseUrl: \"https://api.revkeen.com/v2\" });\nconst result = await client.paymentLinks.deactivate(\"11111111-1111-4111-8111-111111111111\");\nconsole.log(result);"},{"lang":"go","label":"Go (HTTP)","source":"package main\nimport (\"fmt\"; \"io\"; \"net/http\"; \"os\"; \"strings\"; \"time\")\nfunc main() {\n  req, err := http.NewRequest(\"POST\", \"https://api.revkeen.com/v2/payment-links/11111111-1111-4111-8111-111111111111/deactivate\", strings.NewReader(\"\"))\n  if err != nil { panic(err) }\n  req.Header.Set(\"x-api-key\", os.Getenv(\"REVKEEN_API_KEY\"))\n  req.Header.Set(\"Content-Type\", \"application/json\")\n  client := &http.Client{Timeout: 10 * time.Second}\n  response, err := client.Do(req)\n  if err != nil { panic(err) }\n  defer response.Body.Close()\n  if response.StatusCode >= 400 { panic(fmt.Sprintf(\"RevKeen request failed (%d)\", response.StatusCode)) }\n  data, err := io.ReadAll(response.Body)\n  if err != nil { panic(err) }\n  fmt.Println(string(data))\n}"},{"lang":"php","label":"PHP (HTTP)","source":"<?php\n$request = curl_init(\"https://api.revkeen.com/v2/payment-links/11111111-1111-4111-8111-111111111111/deactivate\");\ncurl_setopt_array($request, [\n  CURLOPT_CUSTOMREQUEST => \"POST\",\n  CURLOPT_RETURNTRANSFER => true,\n  CURLOPT_TIMEOUT => 10,\n  CURLOPT_HTTPHEADER => [\"x-api-key: \" . getenv(\"REVKEEN_API_KEY\"), \"Content-Type: application/json\"],\n]);\n$response = curl_exec($request);\nif ($response === false) throw new RuntimeException(curl_error($request));\n$status = curl_getinfo($request, CURLINFO_HTTP_CODE);\ncurl_close($request);\nif ($status >= 400) throw new RuntimeException(\"RevKeen request failed ($status)\");\necho $response;"},{"lang":"python","label":"Python","source":"import os\n\nimport requests\n\nresponse = requests.post(\n    \"https://api.revkeen.com/v2/payment-links/11111111-1111-4111-8111-111111111111/deactivate\",\n    headers={\"x-api-key\": os.environ[\"REVKEEN_API_KEY\"]},\n)\n\nresponse.raise_for_status()\nprint(response.json())"},{"lang":"javascript","label":"JavaScript","source":"const credential = process.env.REVKEEN_API_KEY;\nif (!credential) throw new Error(\"Set REVKEEN_API_KEY\");\nconst response = await fetch(\"https://api.revkeen.com/v2/payment-links/11111111-1111-4111-8111-111111111111/deactivate\", {\n  method: \"POST\",\n  headers: {\n    \"x-api-key\": credential,\n  },\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"java","label":"Java","source":"import java.net.URI;\nimport java.net.http.HttpClient;\nimport java.net.http.HttpRequest;\nimport java.net.http.HttpResponse;\n\nHttpClient client = HttpClient.newHttpClient();\n\nHttpRequest request = HttpRequest.newBuilder()\n    .uri(URI.create(\"https://api.revkeen.com/v2/payment-links/11111111-1111-4111-8111-111111111111/deactivate\"))\n    .header(\"x-api-key\", System.getenv(\"REVKEEN_API_KEY\"))\n    .method(\"POST\", HttpRequest.BodyPublishers.noBody())\n    .build();\n\nHttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());\nSystem.out.println(response.body());"},{"lang":"ruby","label":"Ruby","source":"require \"net/http\"\nrequire \"json\"\nrequire \"uri\"\n\nuri = URI(\"https://api.revkeen.com/v2/payment-links/11111111-1111-4111-8111-111111111111/deactivate\")\nhttp = Net::HTTP.new(uri.host, uri.port)\nhttp.use_ssl = true\n\nrequest = Net::HTTP::Post.new(uri)\nrequest[\"x-api-key\"] = ENV[\"REVKEEN_API_KEY\"]\n\nresponse = http.request(request)\nputs response.body"}],"security":[{"apiKey":["payment_links:write"]},{"oauth2":["payment_links:write"]}]}},"/payment-links/{id}/activate":{"post":{"operationId":"payment_links_activate","tags":["Payment Links"],"summary":"Activate a payment link","description":"Enable a payment link to accept payments. Cannot be used on archived links. Equivalent to `PATCH /v2/payment-links/{id}/status` with `status: \"active\"`.\n\n---\n\n**Related endpoints**\n\n- `POST /payment-links` — Create a new payment link\n- `GET /payment-links` — List payment links\n- `GET /payment-links/{id}` — Get payment link by ID\n- `POST /payment-links/{id}/expire` — Expire a payment link\n- `PATCH /payment-links/{id}/status` — Update payment link status\n- `POST /payment-links/{id}/deactivate` — Deactivate a payment link\n- `POST /payment-links/{id}/archive` — Archive a payment link\n\n**Common errors**\n\n- `400 invalid_request` — malformed payload or failed validation.\n- `401 authentication_error` — missing, invalid, expired, or revoked credential. Codes: `authentication_failed`, `invalid_api_key`, `expired_api_key`, `api_key_revoked`, `session_invalid`, `merchant_required`. Carries a `WWW-Authenticate: Bearer` challenge.\n- `404 resource_missing` — the referenced resource does not exist or is not visible to your key.\n\n**Idempotency**\n\nPass an `Idempotency-Key` header (UUID v4 recommended) to make retries safe. Keys are valid for 24 hours; see [the idempotency guide](/docs/fundamentals/idempotency).","parameters":[{"schema":{"type":"string","format":"uuid","description":"Payment link UUID.","example":"e3e70682-c209-1cac-a29f-6fbed82c07cd"},"required":true,"description":"Payment link UUID.","name":"id","in":"path"}],"responses":{"200":{"description":"Payment link activated","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"status":{"type":"string","enum":["active","inactive","archived"]},"status_changed_at":{"type":"string","format":"date-time"},"is_active":{"type":"boolean"},"updated_at":{"type":"string","format":"date-time"}},"required":["id","status","status_changed_at","is_active","updated_at"]}}}},"400":{"description":"Link is archived and cannot be reactivated","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{}},"required":["error"]}}}},"401":{"description":"Unauthorized","headers":{"WWW-Authenticate":{"description":"RFC 6750 Bearer authentication challenge","schema":{"type":"string"},"example":"Bearer realm=\"revkeen-api\""}}},"404":{"description":"Payment link not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{}},"required":["error"]}}}},"429":{"description":"Too Many Requests - Rate limit exceeded","headers":{"Retry-After":{"description":"Whole seconds until the caller may retry","schema":{"type":"string"},"example":"60"},"X-RateLimit-Limit":{"description":"Request quota for the current window","schema":{"type":"string"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window","schema":{"type":"string"}},"X-RateLimit-Reset":{"description":"UNIX timestamp in seconds when the window resets","schema":{"type":"string"}}}}},"x-cli-group":"payment-links","x-cli-name":"activate","x-codeSamples":[{"lang":"curl","label":"cURL","source":"curl -X POST \"https://api.revkeen.com/v2/payment-links/11111111-1111-4111-8111-111111111111/activate\" \\\n  -H \"x-api-key: $REVKEEN_API_KEY\""},{"lang":"typescript","label":"TypeScript (SDK)","source":"// npm install @revkeen/sdk@1.20260822.1541\nimport { RevKeenClient } from \"@revkeen/sdk\";\nconst apiKey = process.env.REVKEEN_API_KEY;\nif (!apiKey) throw new Error(\"Set REVKEEN_API_KEY\");\nconst client = new RevKeenClient({ apiKey, baseUrl: \"https://api.revkeen.com/v2\" });\nconst result = await client.paymentLinks.activate(\"11111111-1111-4111-8111-111111111111\");\nconsole.log(result);"},{"lang":"go","label":"Go (HTTP)","source":"package main\nimport (\"fmt\"; \"io\"; \"net/http\"; \"os\"; \"strings\"; \"time\")\nfunc main() {\n  req, err := http.NewRequest(\"POST\", \"https://api.revkeen.com/v2/payment-links/11111111-1111-4111-8111-111111111111/activate\", strings.NewReader(\"\"))\n  if err != nil { panic(err) }\n  req.Header.Set(\"x-api-key\", os.Getenv(\"REVKEEN_API_KEY\"))\n  req.Header.Set(\"Content-Type\", \"application/json\")\n  client := &http.Client{Timeout: 10 * time.Second}\n  response, err := client.Do(req)\n  if err != nil { panic(err) }\n  defer response.Body.Close()\n  if response.StatusCode >= 400 { panic(fmt.Sprintf(\"RevKeen request failed (%d)\", response.StatusCode)) }\n  data, err := io.ReadAll(response.Body)\n  if err != nil { panic(err) }\n  fmt.Println(string(data))\n}"},{"lang":"php","label":"PHP (HTTP)","source":"<?php\n$request = curl_init(\"https://api.revkeen.com/v2/payment-links/11111111-1111-4111-8111-111111111111/activate\");\ncurl_setopt_array($request, [\n  CURLOPT_CUSTOMREQUEST => \"POST\",\n  CURLOPT_RETURNTRANSFER => true,\n  CURLOPT_TIMEOUT => 10,\n  CURLOPT_HTTPHEADER => [\"x-api-key: \" . getenv(\"REVKEEN_API_KEY\"), \"Content-Type: application/json\"],\n]);\n$response = curl_exec($request);\nif ($response === false) throw new RuntimeException(curl_error($request));\n$status = curl_getinfo($request, CURLINFO_HTTP_CODE);\ncurl_close($request);\nif ($status >= 400) throw new RuntimeException(\"RevKeen request failed ($status)\");\necho $response;"},{"lang":"python","label":"Python","source":"import os\n\nimport requests\n\nresponse = requests.post(\n    \"https://api.revkeen.com/v2/payment-links/11111111-1111-4111-8111-111111111111/activate\",\n    headers={\"x-api-key\": os.environ[\"REVKEEN_API_KEY\"]},\n)\n\nresponse.raise_for_status()\nprint(response.json())"},{"lang":"javascript","label":"JavaScript","source":"const credential = process.env.REVKEEN_API_KEY;\nif (!credential) throw new Error(\"Set REVKEEN_API_KEY\");\nconst response = await fetch(\"https://api.revkeen.com/v2/payment-links/11111111-1111-4111-8111-111111111111/activate\", {\n  method: \"POST\",\n  headers: {\n    \"x-api-key\": credential,\n  },\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"java","label":"Java","source":"import java.net.URI;\nimport java.net.http.HttpClient;\nimport java.net.http.HttpRequest;\nimport java.net.http.HttpResponse;\n\nHttpClient client = HttpClient.newHttpClient();\n\nHttpRequest request = HttpRequest.newBuilder()\n    .uri(URI.create(\"https://api.revkeen.com/v2/payment-links/11111111-1111-4111-8111-111111111111/activate\"))\n    .header(\"x-api-key\", System.getenv(\"REVKEEN_API_KEY\"))\n    .method(\"POST\", HttpRequest.BodyPublishers.noBody())\n    .build();\n\nHttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());\nSystem.out.println(response.body());"},{"lang":"ruby","label":"Ruby","source":"require \"net/http\"\nrequire \"json\"\nrequire \"uri\"\n\nuri = URI(\"https://api.revkeen.com/v2/payment-links/11111111-1111-4111-8111-111111111111/activate\")\nhttp = Net::HTTP.new(uri.host, uri.port)\nhttp.use_ssl = true\n\nrequest = Net::HTTP::Post.new(uri)\nrequest[\"x-api-key\"] = ENV[\"REVKEEN_API_KEY\"]\n\nresponse = http.request(request)\nputs response.body"}],"security":[{"apiKey":["payment_links:write"]},{"oauth2":["payment_links:write"]}]}},"/payment-links/{id}/archive":{"post":{"operationId":"payment_links_archive","tags":["Payment Links"],"summary":"Archive a payment link","description":"Permanently disable a payment link. This action cannot be undone. Equivalent to `PATCH /v2/payment-links/{id}/status` with `status: \"archived\"`.\n\n---\n\n**Related endpoints**\n\n- `POST /payment-links` — Create a new payment link\n- `GET /payment-links` — List payment links\n- `GET /payment-links/{id}` — Get payment link by ID\n- `POST /payment-links/{id}/expire` — Expire a payment link\n- `PATCH /payment-links/{id}/status` — Update payment link status\n- `POST /payment-links/{id}/deactivate` — Deactivate a payment link\n- `POST /payment-links/{id}/activate` — Activate a payment link\n\n**Common errors**\n\n- `401 authentication_error` — missing, invalid, expired, or revoked credential. Codes: `authentication_failed`, `invalid_api_key`, `expired_api_key`, `api_key_revoked`, `session_invalid`, `merchant_required`. Carries a `WWW-Authenticate: Bearer` challenge.\n- `404 resource_missing` — the referenced resource does not exist or is not visible to your key.\n\n**Idempotency**\n\nPass an `Idempotency-Key` header (UUID v4 recommended) to make retries safe. Keys are valid for 24 hours; see [the idempotency guide](/docs/fundamentals/idempotency).","parameters":[{"schema":{"type":"string","format":"uuid","description":"Payment link UUID.","example":"e3e70682-c209-1cac-a29f-6fbed82c07cd"},"required":true,"description":"Payment link UUID.","name":"id","in":"path"}],"responses":{"200":{"description":"Payment link archived","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"status":{"type":"string","enum":["active","inactive","archived"]},"status_changed_at":{"type":"string","format":"date-time"},"is_active":{"type":"boolean"},"updated_at":{"type":"string","format":"date-time"}},"required":["id","status","status_changed_at","is_active","updated_at"]}}}},"401":{"description":"Unauthorized","headers":{"WWW-Authenticate":{"description":"RFC 6750 Bearer authentication challenge","schema":{"type":"string"},"example":"Bearer realm=\"revkeen-api\""}}},"404":{"description":"Payment link not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"},"details":{}},"required":["error"]}}}},"429":{"description":"Too Many Requests - Rate limit exceeded","headers":{"Retry-After":{"description":"Whole seconds until the caller may retry","schema":{"type":"string"},"example":"60"},"X-RateLimit-Limit":{"description":"Request quota for the current window","schema":{"type":"string"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window","schema":{"type":"string"}},"X-RateLimit-Reset":{"description":"UNIX timestamp in seconds when the window resets","schema":{"type":"string"}}}}},"x-cli-group":"payment-links","x-cli-name":"archive","x-codeSamples":[{"lang":"curl","label":"cURL","source":"curl -X POST \"https://api.revkeen.com/v2/payment-links/11111111-1111-4111-8111-111111111111/archive\" \\\n  -H \"x-api-key: $REVKEEN_API_KEY\""},{"lang":"typescript","label":"TypeScript (SDK)","source":"// npm install @revkeen/sdk@1.20260822.1541\nimport { RevKeenClient } from \"@revkeen/sdk\";\nconst apiKey = process.env.REVKEEN_API_KEY;\nif (!apiKey) throw new Error(\"Set REVKEEN_API_KEY\");\nconst client = new RevKeenClient({ apiKey, baseUrl: \"https://api.revkeen.com/v2\" });\nconst result = await client.paymentLinks.archive(\"11111111-1111-4111-8111-111111111111\");\nconsole.log(result);"},{"lang":"go","label":"Go (HTTP)","source":"package main\nimport (\"fmt\"; \"io\"; \"net/http\"; \"os\"; \"strings\"; \"time\")\nfunc main() {\n  req, err := http.NewRequest(\"POST\", \"https://api.revkeen.com/v2/payment-links/11111111-1111-4111-8111-111111111111/archive\", strings.NewReader(\"\"))\n  if err != nil { panic(err) }\n  req.Header.Set(\"x-api-key\", os.Getenv(\"REVKEEN_API_KEY\"))\n  req.Header.Set(\"Content-Type\", \"application/json\")\n  client := &http.Client{Timeout: 10 * time.Second}\n  response, err := client.Do(req)\n  if err != nil { panic(err) }\n  defer response.Body.Close()\n  if response.StatusCode >= 400 { panic(fmt.Sprintf(\"RevKeen request failed (%d)\", response.StatusCode)) }\n  data, err := io.ReadAll(response.Body)\n  if err != nil { panic(err) }\n  fmt.Println(string(data))\n}"},{"lang":"php","label":"PHP (HTTP)","source":"<?php\n$request = curl_init(\"https://api.revkeen.com/v2/payment-links/11111111-1111-4111-8111-111111111111/archive\");\ncurl_setopt_array($request, [\n  CURLOPT_CUSTOMREQUEST => \"POST\",\n  CURLOPT_RETURNTRANSFER => true,\n  CURLOPT_TIMEOUT => 10,\n  CURLOPT_HTTPHEADER => [\"x-api-key: \" . getenv(\"REVKEEN_API_KEY\"), \"Content-Type: application/json\"],\n]);\n$response = curl_exec($request);\nif ($response === false) throw new RuntimeException(curl_error($request));\n$status = curl_getinfo($request, CURLINFO_HTTP_CODE);\ncurl_close($request);\nif ($status >= 400) throw new RuntimeException(\"RevKeen request failed ($status)\");\necho $response;"},{"lang":"python","label":"Python","source":"import os\n\nimport requests\n\nresponse = requests.post(\n    \"https://api.revkeen.com/v2/payment-links/11111111-1111-4111-8111-111111111111/archive\",\n    headers={\"x-api-key\": os.environ[\"REVKEEN_API_KEY\"]},\n)\n\nresponse.raise_for_status()\nprint(response.json())"},{"lang":"javascript","label":"JavaScript","source":"const credential = process.env.REVKEEN_API_KEY;\nif (!credential) throw new Error(\"Set REVKEEN_API_KEY\");\nconst response = await fetch(\"https://api.revkeen.com/v2/payment-links/11111111-1111-4111-8111-111111111111/archive\", {\n  method: \"POST\",\n  headers: {\n    \"x-api-key\": credential,\n  },\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"java","label":"Java","source":"import java.net.URI;\nimport java.net.http.HttpClient;\nimport java.net.http.HttpRequest;\nimport java.net.http.HttpResponse;\n\nHttpClient client = HttpClient.newHttpClient();\n\nHttpRequest request = HttpRequest.newBuilder()\n    .uri(URI.create(\"https://api.revkeen.com/v2/payment-links/11111111-1111-4111-8111-111111111111/archive\"))\n    .header(\"x-api-key\", System.getenv(\"REVKEEN_API_KEY\"))\n    .method(\"POST\", HttpRequest.BodyPublishers.noBody())\n    .build();\n\nHttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());\nSystem.out.println(response.body());"},{"lang":"ruby","label":"Ruby","source":"require \"net/http\"\nrequire \"json\"\nrequire \"uri\"\n\nuri = URI(\"https://api.revkeen.com/v2/payment-links/11111111-1111-4111-8111-111111111111/archive\")\nhttp = Net::HTTP.new(uri.host, uri.port)\nhttp.use_ssl = true\n\nrequest = Net::HTTP::Post.new(uri)\nrequest[\"x-api-key\"] = ENV[\"REVKEEN_API_KEY\"]\n\nresponse = http.request(request)\nputs response.body"}],"security":[{"apiKey":["payment_links:write"]},{"oauth2":["payment_links:write"]}]}},"/billing/overview":{"get":{"operationId":"billing_overview_get","tags":["Billing"],"x-fern-sdk-group-name":["billing"],"x-fern-sdk-method-name":"overview","summary":"Get merchant billing overview","description":"Return overdue invoices, subscriptions in dunning, and high-signal usage invoices for the authenticated merchant. Use this instead of chaining invoices_list + subscriptions_list_in_dunning.\n\n---\n\n**Common errors**\n\n- `401 authentication_error` — missing, invalid, expired, or revoked credential. Codes: `authentication_failed`, `invalid_api_key`, `expired_api_key`, `api_key_revoked`, `session_invalid`, `merchant_required`. Carries a `WWW-Authenticate: Bearer` challenge.","responses":{"200":{"description":"Billing overview snapshot","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/BillingOverviewResponse"}},"required":["data"]}}}},"401":{"description":"Unauthorized","headers":{"WWW-Authenticate":{"description":"RFC 6750 Bearer authentication challenge","schema":{"type":"string"},"example":"Bearer realm=\"revkeen-api\""}}},"429":{"description":"Too Many Requests - Rate limit exceeded","headers":{"Retry-After":{"description":"Whole seconds until the caller may retry","schema":{"type":"string"},"example":"60"},"X-RateLimit-Limit":{"description":"Request quota for the current window","schema":{"type":"string"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window","schema":{"type":"string"}},"X-RateLimit-Reset":{"description":"UNIX timestamp in seconds when the window resets","schema":{"type":"string"}}}}},"x-cli-group":"billing","x-cli-name":"overview-get","x-codeSamples":[{"lang":"curl","label":"cURL","source":"curl -X GET \"https://api.revkeen.com/v2/billing/overview\" \\\n  -H \"x-api-key: $REVKEEN_API_KEY\""},{"lang":"typescript","label":"TypeScript (HTTP)","source":"const credential = process.env.REVKEEN_API_KEY;\nif (!credential) throw new Error(\"Set REVKEEN_API_KEY\");\nconst response = await fetch(\"https://api.revkeen.com/v2/billing/overview\", {\n  method: \"GET\",\n  headers: {\n    \"x-api-key\": credential,\n  },\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"go","label":"Go (HTTP)","source":"package main\nimport (\"fmt\"; \"io\"; \"net/http\"; \"os\"; \"strings\"; \"time\")\nfunc main() {\n  req, err := http.NewRequest(\"GET\", \"https://api.revkeen.com/v2/billing/overview\", strings.NewReader(\"\"))\n  if err != nil { panic(err) }\n  req.Header.Set(\"x-api-key\", os.Getenv(\"REVKEEN_API_KEY\"))\n  req.Header.Set(\"Content-Type\", \"application/json\")\n  client := &http.Client{Timeout: 10 * time.Second}\n  response, err := client.Do(req)\n  if err != nil { panic(err) }\n  defer response.Body.Close()\n  if response.StatusCode >= 400 { panic(fmt.Sprintf(\"RevKeen request failed (%d)\", response.StatusCode)) }\n  data, err := io.ReadAll(response.Body)\n  if err != nil { panic(err) }\n  fmt.Println(string(data))\n}"},{"lang":"php","label":"PHP (HTTP)","source":"<?php\n$request = curl_init(\"https://api.revkeen.com/v2/billing/overview\");\ncurl_setopt_array($request, [\n  CURLOPT_CUSTOMREQUEST => \"GET\",\n  CURLOPT_RETURNTRANSFER => true,\n  CURLOPT_TIMEOUT => 10,\n  CURLOPT_HTTPHEADER => [\"x-api-key: \" . getenv(\"REVKEEN_API_KEY\"), \"Content-Type: application/json\"],\n]);\n$response = curl_exec($request);\nif ($response === false) throw new RuntimeException(curl_error($request));\n$status = curl_getinfo($request, CURLINFO_HTTP_CODE);\ncurl_close($request);\nif ($status >= 400) throw new RuntimeException(\"RevKeen request failed ($status)\");\necho $response;"},{"lang":"python","label":"Python","source":"import os\n\nimport requests\n\nresponse = requests.get(\n    \"https://api.revkeen.com/v2/billing/overview\",\n    headers={\"x-api-key\": os.environ[\"REVKEEN_API_KEY\"]},\n)\n\nresponse.raise_for_status()\nprint(response.json())"},{"lang":"javascript","label":"JavaScript","source":"const credential = process.env.REVKEEN_API_KEY;\nif (!credential) throw new Error(\"Set REVKEEN_API_KEY\");\nconst response = await fetch(\"https://api.revkeen.com/v2/billing/overview\", {\n  method: \"GET\",\n  headers: {\n    \"x-api-key\": credential,\n  },\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"java","label":"Java","source":"import java.net.URI;\nimport java.net.http.HttpClient;\nimport java.net.http.HttpRequest;\nimport java.net.http.HttpResponse;\n\nHttpClient client = HttpClient.newHttpClient();\n\nHttpRequest request = HttpRequest.newBuilder()\n    .uri(URI.create(\"https://api.revkeen.com/v2/billing/overview\"))\n    .header(\"x-api-key\", System.getenv(\"REVKEEN_API_KEY\"))\n    .GET()\n    .build();\n\nHttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());\nSystem.out.println(response.body());"},{"lang":"ruby","label":"Ruby","source":"require \"net/http\"\nrequire \"json\"\nrequire \"uri\"\n\nuri = URI(\"https://api.revkeen.com/v2/billing/overview\")\nhttp = Net::HTTP.new(uri.host, uri.port)\nhttp.use_ssl = true\n\nrequest = Net::HTTP::Get.new(uri)\nrequest[\"x-api-key\"] = ENV[\"REVKEEN_API_KEY\"]\n\nresponse = http.request(request)\nputs response.body"}],"security":[{"apiKey":["invoices:read"]},{"oauth2":["invoices:read"]}]}},"/prices":{"get":{"operationId":"prices_list","tags":["Prices"],"x-fern-sdk-group-name":["prices"],"summary":"List prices","description":"List all prices for the authenticated merchant. Supports filtering and cursor-based pagination.\n\n`starting_after` and `ending_before` are mutually exclusive: supply at most one. Sending both is rejected with 400 `parameter_invalid`. A cursor must also name a price belonging to the authenticated merchant; an unknown id is rejected with 400 `resource_missing`.\n\n---\n\n**Related endpoints**\n\n- `POST /prices` — Create a price\n- `GET /prices/{id}` — Retrieve a price\n- `PATCH /prices/{id}` — Update a price\n- `DELETE /prices/{id}` — Archive a price\n\n**Common errors**\n\n- `400 invalid_request` — malformed payload or failed validation.\n- `401 authentication_error` — missing, invalid, expired, or revoked credential. Codes: `authentication_failed`, `invalid_api_key`, `expired_api_key`, `api_key_revoked`, `session_invalid`, `merchant_required`. Carries a `WWW-Authenticate: Bearer` challenge.\n\n**Pagination**\n\nOffset-based with `limit` (default 25, max 100) and `offset`. The response `pagination` block includes `total` and `hasMore`. See [the pagination guide](/docs/fundamentals/pagination) for SDK auto-paging helpers.","parameters":[{"schema":{"type":"string","format":"uuid","description":"Filter by product ID"},"required":false,"description":"Filter by product ID","name":"product_id","in":"query"},{"schema":{"type":"string","enum":["one_time","recurring"],"description":"Filter by price type"},"required":false,"description":"Filter by price type","name":"type","in":"query"},{"schema":{"type":"string","enum":["fixed","pay_what_you_want","free"],"description":"Filter by pricing model"},"required":false,"description":"Filter by pricing model","name":"pricing_model","in":"query"},{"schema":{"type":"string","enum":["true","false"],"description":"Filter by active status"},"required":false,"description":"Filter by active status","name":"active","in":"query"},{"schema":{"type":"string","minLength":3,"maxLength":3,"description":"Filter by currency"},"required":false,"description":"Filter by currency","name":"currency","in":"query"},{"schema":{"type":"string","description":"Filter by lookup key"},"required":false,"description":"Filter by lookup key","name":"lookup_key","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":100,"default":20,"description":"Number of results (1-100)"},"required":false,"description":"Number of results (1-100)","name":"limit","in":"query"},{"schema":{"type":"string","format":"uuid","description":"Cursor for pagination: return prices after this price ID. Mutually exclusive with `ending_before` — supplying both is rejected with 400 `parameter_invalid`. The ID must name a price of the authenticated merchant, or the request is rejected with 400 `resource_missing`."},"required":false,"description":"Cursor for pagination: return prices after this price ID. Mutually exclusive with `ending_before` — supplying both is rejected with 400 `parameter_invalid`. The ID must name a price of the authenticated merchant, or the request is rejected with 400 `resource_missing`.","name":"starting_after","in":"query"},{"schema":{"type":"string","format":"uuid","description":"Cursor for pagination: return prices before this price ID. Mutually exclusive with `starting_after` — supplying both is rejected with 400 `parameter_invalid`. The ID must name a price of the authenticated merchant, or the request is rejected with 400 `resource_missing`."},"required":false,"description":"Cursor for pagination: return prices before this price ID. Mutually exclusive with `starting_after` — supplying both is rejected with 400 `parameter_invalid`. The ID must name a price of the authenticated merchant, or the request is rejected with 400 `resource_missing`.","name":"ending_before","in":"query"}],"responses":{"200":{"description":"List of prices","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PriceListResponse"}}}},"400":{"description":"`starting_after` and `ending_before` were combined, or a cursor named no price of this merchant","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListCursorError"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PriceErrorResponse"}}},"headers":{"WWW-Authenticate":{"description":"RFC 6750 Bearer authentication challenge","schema":{"type":"string"},"example":"Bearer realm=\"revkeen-api\""}}},"429":{"description":"Too Many Requests - Rate limit exceeded","headers":{"Retry-After":{"description":"Whole seconds until the caller may retry","schema":{"type":"string"},"example":"60"},"X-RateLimit-Limit":{"description":"Request quota for the current window","schema":{"type":"string"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window","schema":{"type":"string"}},"X-RateLimit-Reset":{"description":"UNIX timestamp in seconds when the window resets","schema":{"type":"string"}}}}},"x-cli-group":"prices","x-cli-name":"list","x-codeSamples":[{"lang":"curl","label":"cURL","source":"curl -X GET \"https://api.revkeen.com/v2/prices?product_id=11111111-1111-4111-8111-111111111111&type=one_time\" \\\n  -H \"x-api-key: $REVKEEN_API_KEY\""},{"lang":"typescript","label":"TypeScript (SDK)","source":"// npm install @revkeen/sdk@1.20260822.1541\nimport { RevKeenClient } from \"@revkeen/sdk\";\nconst apiKey = process.env.REVKEEN_API_KEY;\nif (!apiKey) throw new Error(\"Set REVKEEN_API_KEY\");\nconst client = new RevKeenClient({ apiKey, baseUrl: \"https://api.revkeen.com/v2\" });\nconst result = await client.prices.list({\n  \"product_id\": \"11111111-1111-4111-8111-111111111111\",\n  \"type\": \"one_time\"\n});\nconsole.log(result);"},{"lang":"go","label":"Go (HTTP)","source":"package main\nimport (\"fmt\"; \"io\"; \"net/http\"; \"os\"; \"strings\"; \"time\")\nfunc main() {\n  req, err := http.NewRequest(\"GET\", \"https://api.revkeen.com/v2/prices?product_id=11111111-1111-4111-8111-111111111111&type=one_time\", strings.NewReader(\"\"))\n  if err != nil { panic(err) }\n  req.Header.Set(\"x-api-key\", os.Getenv(\"REVKEEN_API_KEY\"))\n  req.Header.Set(\"Content-Type\", \"application/json\")\n  client := &http.Client{Timeout: 10 * time.Second}\n  response, err := client.Do(req)\n  if err != nil { panic(err) }\n  defer response.Body.Close()\n  if response.StatusCode >= 400 { panic(fmt.Sprintf(\"RevKeen request failed (%d)\", response.StatusCode)) }\n  data, err := io.ReadAll(response.Body)\n  if err != nil { panic(err) }\n  fmt.Println(string(data))\n}"},{"lang":"php","label":"PHP (HTTP)","source":"<?php\n$request = curl_init(\"https://api.revkeen.com/v2/prices?product_id=11111111-1111-4111-8111-111111111111&type=one_time\");\ncurl_setopt_array($request, [\n  CURLOPT_CUSTOMREQUEST => \"GET\",\n  CURLOPT_RETURNTRANSFER => true,\n  CURLOPT_TIMEOUT => 10,\n  CURLOPT_HTTPHEADER => [\"x-api-key: \" . getenv(\"REVKEEN_API_KEY\"), \"Content-Type: application/json\"],\n]);\n$response = curl_exec($request);\nif ($response === false) throw new RuntimeException(curl_error($request));\n$status = curl_getinfo($request, CURLINFO_HTTP_CODE);\ncurl_close($request);\nif ($status >= 400) throw new RuntimeException(\"RevKeen request failed ($status)\");\necho $response;"},{"lang":"python","label":"Python","source":"import os\n\nimport requests\n\nresponse = requests.get(\n    \"https://api.revkeen.com/v2/prices\",\n    headers={\"x-api-key\": os.environ[\"REVKEEN_API_KEY\"]},\n    params={\n        \"product_id\": \"11111111-1111-4111-8111-111111111111\",\n        \"type\": \"one_time\",\n    },\n)\n\nresponse.raise_for_status()\nprint(response.json())"},{"lang":"javascript","label":"JavaScript","source":"const credential = process.env.REVKEEN_API_KEY;\nif (!credential) throw new Error(\"Set REVKEEN_API_KEY\");\nconst response = await fetch(\"https://api.revkeen.com/v2/prices?product_id=11111111-1111-4111-8111-111111111111&type=one_time\", {\n  method: \"GET\",\n  headers: {\n    \"x-api-key\": credential,\n  },\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"java","label":"Java","source":"import java.net.URI;\nimport java.net.http.HttpClient;\nimport java.net.http.HttpRequest;\nimport java.net.http.HttpResponse;\n\nHttpClient client = HttpClient.newHttpClient();\n\nHttpRequest request = HttpRequest.newBuilder()\n    .uri(URI.create(\"https://api.revkeen.com/v2/prices?product_id=11111111-1111-4111-8111-111111111111&type=one_time\"))\n    .header(\"x-api-key\", System.getenv(\"REVKEEN_API_KEY\"))\n    .GET()\n    .build();\n\nHttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());\nSystem.out.println(response.body());"},{"lang":"ruby","label":"Ruby","source":"require \"net/http\"\nrequire \"json\"\nrequire \"uri\"\n\nuri = URI(\"https://api.revkeen.com/v2/prices?product_id=11111111-1111-4111-8111-111111111111&type=one_time\")\nhttp = Net::HTTP.new(uri.host, uri.port)\nhttp.use_ssl = true\n\nrequest = Net::HTTP::Get.new(uri)\nrequest[\"x-api-key\"] = ENV[\"REVKEEN_API_KEY\"]\n\nresponse = http.request(request)\nputs response.body"}],"security":[{"apiKey":["prices:read"]},{"oauth2":["prices:read"]}]},"post":{"operationId":"prices_create","tags":["Prices"],"x-fern-sdk-group-name":["prices"],"summary":"Create a price","description":"Creates a new price for a product. Supports fixed, pay-what-you-want, and free pricing models.\n\n---\n\n**Related endpoints**\n\n- `GET /prices` — List prices\n- `GET /prices/{id}` — Retrieve a price\n- `PATCH /prices/{id}` — Update a price\n- `DELETE /prices/{id}` — Archive a price\n\n**Common errors**\n\n- `400 invalid_request` — malformed payload or failed validation.\n- `401 authentication_error` — missing, invalid, expired, or revoked credential. Codes: `authentication_failed`, `invalid_api_key`, `expired_api_key`, `api_key_revoked`, `session_invalid`, `merchant_required`. Carries a `WWW-Authenticate: Bearer` challenge.\n- `404 resource_missing` — the referenced resource does not exist or is not visible to your key.\n\n**Idempotency**\n\nPass an `Idempotency-Key` header (UUID v4 recommended) to make retries safe. Keys are valid for 24 hours; see [the idempotency guide](/docs/fundamentals/idempotency).","requestBody":{"description":"Price creation details","required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreatePriceRequest"}}}},"responses":{"201":{"description":"Price created successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Price"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PriceErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PriceErrorResponse"}}},"headers":{"WWW-Authenticate":{"description":"RFC 6750 Bearer authentication challenge","schema":{"type":"string"},"example":"Bearer realm=\"revkeen-api\""}}},"404":{"description":"Product not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PriceErrorResponse"}}}},"429":{"description":"Too Many Requests - Rate limit exceeded","headers":{"Retry-After":{"description":"Whole seconds until the caller may retry","schema":{"type":"string"},"example":"60"},"X-RateLimit-Limit":{"description":"Request quota for the current window","schema":{"type":"string"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window","schema":{"type":"string"}},"X-RateLimit-Reset":{"description":"UNIX timestamp in seconds when the window resets","schema":{"type":"string"}}}}},"x-cli-group":"prices","x-cli-name":"create","x-codeSamples":[{"lang":"curl","label":"cURL","source":"curl -X POST \"https://api.revkeen.com/v2/prices\" \\\n  -H \"x-api-key: $REVKEEN_API_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"product_id\": \"11111111-1111-4111-8111-111111111111\"\n  }'"},{"lang":"typescript","label":"TypeScript (SDK)","source":"// npm install @revkeen/sdk@1.20260822.1541\nimport { RevKeenClient } from \"@revkeen/sdk\";\nconst apiKey = process.env.REVKEEN_API_KEY;\nif (!apiKey) throw new Error(\"Set REVKEEN_API_KEY\");\nconst client = new RevKeenClient({ apiKey, baseUrl: \"https://api.revkeen.com/v2\" });\nconst result = await client.prices.create({\n  \"product_id\": \"11111111-1111-4111-8111-111111111111\"\n});\nconsole.log(result);"},{"lang":"go","label":"Go (HTTP)","source":"package main\nimport (\"fmt\"; \"io\"; \"net/http\"; \"os\"; \"strings\"; \"time\")\nfunc main() {\n  req, err := http.NewRequest(\"POST\", \"https://api.revkeen.com/v2/prices\", strings.NewReader(\"{\\\"product_id\\\":\\\"11111111-1111-4111-8111-111111111111\\\"}\"))\n  if err != nil { panic(err) }\n  req.Header.Set(\"x-api-key\", os.Getenv(\"REVKEEN_API_KEY\"))\n  req.Header.Set(\"Content-Type\", \"application/json\")\n  client := &http.Client{Timeout: 10 * time.Second}\n  response, err := client.Do(req)\n  if err != nil { panic(err) }\n  defer response.Body.Close()\n  if response.StatusCode >= 400 { panic(fmt.Sprintf(\"RevKeen request failed (%d)\", response.StatusCode)) }\n  data, err := io.ReadAll(response.Body)\n  if err != nil { panic(err) }\n  fmt.Println(string(data))\n}"},{"lang":"php","label":"PHP (HTTP)","source":"<?php\n$request = curl_init(\"https://api.revkeen.com/v2/prices\");\ncurl_setopt_array($request, [\n  CURLOPT_CUSTOMREQUEST => \"POST\",\n  CURLOPT_RETURNTRANSFER => true,\n  CURLOPT_TIMEOUT => 10,\n  CURLOPT_HTTPHEADER => [\"x-api-key: \" . getenv(\"REVKEEN_API_KEY\"), \"Content-Type: application/json\"],\n  CURLOPT_POSTFIELDS => '{\\\"product_id\\\":\\\"11111111-1111-4111-8111-111111111111\\\"}',\n]);\n$response = curl_exec($request);\nif ($response === false) throw new RuntimeException(curl_error($request));\n$status = curl_getinfo($request, CURLINFO_HTTP_CODE);\ncurl_close($request);\nif ($status >= 400) throw new RuntimeException(\"RevKeen request failed ($status)\");\necho $response;"},{"lang":"python","label":"Python","source":"import os\n\nimport requests\n\nresponse = requests.post(\n    \"https://api.revkeen.com/v2/prices\",\n    headers={\"x-api-key\": os.environ[\"REVKEEN_API_KEY\"]},\n    json={\n        \"product_id\": \"11111111-1111-4111-8111-111111111111\",\n    },\n)\n\nresponse.raise_for_status()\nprint(response.json())"},{"lang":"javascript","label":"JavaScript","source":"const credential = process.env.REVKEEN_API_KEY;\nif (!credential) throw new Error(\"Set REVKEEN_API_KEY\");\nconst response = await fetch(\"https://api.revkeen.com/v2/prices\", {\n  method: \"POST\",\n  headers: {\n    \"x-api-key\": credential,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n    \"product_id\": \"11111111-1111-4111-8111-111111111111\"\n  }),\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"java","label":"Java","source":"import java.net.URI;\nimport java.net.http.HttpClient;\nimport java.net.http.HttpRequest;\nimport java.net.http.HttpResponse;\n\nHttpClient client = HttpClient.newHttpClient();\n\nHttpRequest request = HttpRequest.newBuilder()\n    .uri(URI.create(\"https://api.revkeen.com/v2/prices\"))\n    .header(\"x-api-key\", System.getenv(\"REVKEEN_API_KEY\"))\n    .header(\"Content-Type\", \"application/json\")\n    .method(\"POST\", HttpRequest.BodyPublishers.ofString(\"\"\"\n        {\n          \"product_id\": \"11111111-1111-4111-8111-111111111111\"\n        }\n        \"\"\"))\n    .build();\n\nHttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());\nSystem.out.println(response.body());"},{"lang":"ruby","label":"Ruby","source":"require \"net/http\"\nrequire \"json\"\nrequire \"uri\"\n\nuri = URI(\"https://api.revkeen.com/v2/prices\")\nhttp = Net::HTTP.new(uri.host, uri.port)\nhttp.use_ssl = true\n\nrequest = Net::HTTP::Post.new(uri)\nrequest[\"x-api-key\"] = ENV[\"REVKEEN_API_KEY\"]\nrequest[\"Content-Type\"] = \"application/json\"\nrequest.body = {\n  \"product_id\" => \"11111111-1111-4111-8111-111111111111\",\n}.to_json\n\nresponse = http.request(request)\nputs response.body"}],"security":[{"apiKey":["prices:write"]},{"oauth2":["prices:write"]}]}},"/prices/{id}":{"get":{"operationId":"prices_get","tags":["Prices"],"x-fern-sdk-group-name":["prices"],"summary":"Retrieve a price","description":"Retrieves a price by its ID.\n\n---\n\n**Related endpoints**\n\n- `GET /prices` — List prices\n- `POST /prices` — Create a price\n- `PATCH /prices/{id}` — Update a price\n- `DELETE /prices/{id}` — Archive a price\n\n**Common errors**\n\n- `401 authentication_error` — missing, invalid, expired, or revoked credential. Codes: `authentication_failed`, `invalid_api_key`, `expired_api_key`, `api_key_revoked`, `session_invalid`, `merchant_required`. Carries a `WWW-Authenticate: Bearer` challenge.\n- `404 resource_missing` — the referenced resource does not exist or is not visible to your key.","parameters":[{"schema":{"type":"string","format":"uuid","description":"Price ID"},"required":true,"description":"Price ID","name":"id","in":"path"}],"responses":{"200":{"description":"Price details","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Price"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PriceErrorResponse"}}},"headers":{"WWW-Authenticate":{"description":"RFC 6750 Bearer authentication challenge","schema":{"type":"string"},"example":"Bearer realm=\"revkeen-api\""}}},"404":{"description":"Price not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PriceErrorResponse"}}}},"429":{"description":"Too Many Requests - Rate limit exceeded","headers":{"Retry-After":{"description":"Whole seconds until the caller may retry","schema":{"type":"string"},"example":"60"},"X-RateLimit-Limit":{"description":"Request quota for the current window","schema":{"type":"string"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window","schema":{"type":"string"}},"X-RateLimit-Reset":{"description":"UNIX timestamp in seconds when the window resets","schema":{"type":"string"}}}}},"x-cli-group":"prices","x-cli-name":"get","x-codeSamples":[{"lang":"curl","label":"cURL","source":"curl -X GET \"https://api.revkeen.com/v2/prices/11111111-1111-4111-8111-111111111111\" \\\n  -H \"x-api-key: $REVKEEN_API_KEY\""},{"lang":"typescript","label":"TypeScript (SDK)","source":"// npm install @revkeen/sdk@1.20260822.1541\nimport { RevKeenClient } from \"@revkeen/sdk\";\nconst apiKey = process.env.REVKEEN_API_KEY;\nif (!apiKey) throw new Error(\"Set REVKEEN_API_KEY\");\nconst client = new RevKeenClient({ apiKey, baseUrl: \"https://api.revkeen.com/v2\" });\nconst result = await client.prices.get(\"11111111-1111-4111-8111-111111111111\");\nconsole.log(result);"},{"lang":"go","label":"Go (HTTP)","source":"package main\nimport (\"fmt\"; \"io\"; \"net/http\"; \"os\"; \"strings\"; \"time\")\nfunc main() {\n  req, err := http.NewRequest(\"GET\", \"https://api.revkeen.com/v2/prices/11111111-1111-4111-8111-111111111111\", strings.NewReader(\"\"))\n  if err != nil { panic(err) }\n  req.Header.Set(\"x-api-key\", os.Getenv(\"REVKEEN_API_KEY\"))\n  req.Header.Set(\"Content-Type\", \"application/json\")\n  client := &http.Client{Timeout: 10 * time.Second}\n  response, err := client.Do(req)\n  if err != nil { panic(err) }\n  defer response.Body.Close()\n  if response.StatusCode >= 400 { panic(fmt.Sprintf(\"RevKeen request failed (%d)\", response.StatusCode)) }\n  data, err := io.ReadAll(response.Body)\n  if err != nil { panic(err) }\n  fmt.Println(string(data))\n}"},{"lang":"php","label":"PHP (HTTP)","source":"<?php\n$request = curl_init(\"https://api.revkeen.com/v2/prices/11111111-1111-4111-8111-111111111111\");\ncurl_setopt_array($request, [\n  CURLOPT_CUSTOMREQUEST => \"GET\",\n  CURLOPT_RETURNTRANSFER => true,\n  CURLOPT_TIMEOUT => 10,\n  CURLOPT_HTTPHEADER => [\"x-api-key: \" . getenv(\"REVKEEN_API_KEY\"), \"Content-Type: application/json\"],\n]);\n$response = curl_exec($request);\nif ($response === false) throw new RuntimeException(curl_error($request));\n$status = curl_getinfo($request, CURLINFO_HTTP_CODE);\ncurl_close($request);\nif ($status >= 400) throw new RuntimeException(\"RevKeen request failed ($status)\");\necho $response;"},{"lang":"python","label":"Python","source":"import os\n\nimport requests\n\nresponse = requests.get(\n    \"https://api.revkeen.com/v2/prices/11111111-1111-4111-8111-111111111111\",\n    headers={\"x-api-key\": os.environ[\"REVKEEN_API_KEY\"]},\n)\n\nresponse.raise_for_status()\nprint(response.json())"},{"lang":"javascript","label":"JavaScript","source":"const credential = process.env.REVKEEN_API_KEY;\nif (!credential) throw new Error(\"Set REVKEEN_API_KEY\");\nconst response = await fetch(\"https://api.revkeen.com/v2/prices/11111111-1111-4111-8111-111111111111\", {\n  method: \"GET\",\n  headers: {\n    \"x-api-key\": credential,\n  },\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"java","label":"Java","source":"import java.net.URI;\nimport java.net.http.HttpClient;\nimport java.net.http.HttpRequest;\nimport java.net.http.HttpResponse;\n\nHttpClient client = HttpClient.newHttpClient();\n\nHttpRequest request = HttpRequest.newBuilder()\n    .uri(URI.create(\"https://api.revkeen.com/v2/prices/11111111-1111-4111-8111-111111111111\"))\n    .header(\"x-api-key\", System.getenv(\"REVKEEN_API_KEY\"))\n    .GET()\n    .build();\n\nHttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());\nSystem.out.println(response.body());"},{"lang":"ruby","label":"Ruby","source":"require \"net/http\"\nrequire \"json\"\nrequire \"uri\"\n\nuri = URI(\"https://api.revkeen.com/v2/prices/11111111-1111-4111-8111-111111111111\")\nhttp = Net::HTTP.new(uri.host, uri.port)\nhttp.use_ssl = true\n\nrequest = Net::HTTP::Get.new(uri)\nrequest[\"x-api-key\"] = ENV[\"REVKEEN_API_KEY\"]\n\nresponse = http.request(request)\nputs response.body"}],"security":[{"apiKey":["prices:read"]},{"oauth2":["prices:read"]}]},"patch":{"operationId":"prices_update","tags":["Prices"],"x-fern-sdk-group-name":["prices"],"summary":"Update a price","description":"Updates a price. Only certain fields can be updated (active, name, nickname, lookup_key, metadata). Core pricing fields are immutable.\n\n---\n\n**Related endpoints**\n\n- `GET /prices` — List prices\n- `POST /prices` — Create a price\n- `GET /prices/{id}` — Retrieve a price\n- `DELETE /prices/{id}` — Archive a price\n\n**Common errors**\n\n- `400 invalid_request` — malformed payload or failed validation.\n- `401 authentication_error` — missing, invalid, expired, or revoked credential. Codes: `authentication_failed`, `invalid_api_key`, `expired_api_key`, `api_key_revoked`, `session_invalid`, `merchant_required`. Carries a `WWW-Authenticate: Bearer` challenge.\n- `404 resource_missing` — the referenced resource does not exist or is not visible to your key.\n\n**Idempotency**\n\nPass an `Idempotency-Key` header (UUID v4 recommended) to make retries safe. Keys are valid for 24 hours; see [the idempotency guide](/docs/fundamentals/idempotency).","parameters":[{"schema":{"type":"string","format":"uuid","description":"Price ID"},"required":true,"description":"Price ID","name":"id","in":"path"}],"requestBody":{"description":"Price update details","required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdatePriceRequest"}}}},"responses":{"200":{"description":"Price updated successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Price"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PriceErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PriceErrorResponse"}}},"headers":{"WWW-Authenticate":{"description":"RFC 6750 Bearer authentication challenge","schema":{"type":"string"},"example":"Bearer realm=\"revkeen-api\""}}},"404":{"description":"Price not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PriceErrorResponse"}}}},"429":{"description":"Too Many Requests - Rate limit exceeded","headers":{"Retry-After":{"description":"Whole seconds until the caller may retry","schema":{"type":"string"},"example":"60"},"X-RateLimit-Limit":{"description":"Request quota for the current window","schema":{"type":"string"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window","schema":{"type":"string"}},"X-RateLimit-Reset":{"description":"UNIX timestamp in seconds when the window resets","schema":{"type":"string"}}}}},"x-cli-group":"prices","x-cli-name":"update","x-codeSamples":[{"lang":"curl","label":"cURL","source":"curl -X PATCH \"https://api.revkeen.com/v2/prices/11111111-1111-4111-8111-111111111111\" \\\n  -H \"x-api-key: $REVKEEN_API_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"active\": true,\n    \"name\": \"example\",\n    \"nickname\": \"example\",\n    \"description\": \"Example request from API docs\"\n  }'"},{"lang":"typescript","label":"TypeScript (SDK)","source":"// npm install @revkeen/sdk@1.20260822.1541\nimport { RevKeenClient } from \"@revkeen/sdk\";\nconst apiKey = process.env.REVKEEN_API_KEY;\nif (!apiKey) throw new Error(\"Set REVKEEN_API_KEY\");\nconst client = new RevKeenClient({ apiKey, baseUrl: \"https://api.revkeen.com/v2\" });\nconst result = await client.prices.update(\"11111111-1111-4111-8111-111111111111\", {\n  \"active\": true,\n  \"name\": \"example\",\n  \"nickname\": \"example\",\n  \"description\": \"Example request from API docs\"\n});\nconsole.log(result);"},{"lang":"go","label":"Go (HTTP)","source":"package main\nimport (\"fmt\"; \"io\"; \"net/http\"; \"os\"; \"strings\"; \"time\")\nfunc main() {\n  req, err := http.NewRequest(\"PATCH\", \"https://api.revkeen.com/v2/prices/11111111-1111-4111-8111-111111111111\", strings.NewReader(\"{\\\"active\\\":true,\\\"name\\\":\\\"example\\\",\\\"nickname\\\":\\\"example\\\",\\\"description\\\":\\\"Example request from API docs\\\"}\"))\n  if err != nil { panic(err) }\n  req.Header.Set(\"x-api-key\", os.Getenv(\"REVKEEN_API_KEY\"))\n  req.Header.Set(\"Content-Type\", \"application/json\")\n  client := &http.Client{Timeout: 10 * time.Second}\n  response, err := client.Do(req)\n  if err != nil { panic(err) }\n  defer response.Body.Close()\n  if response.StatusCode >= 400 { panic(fmt.Sprintf(\"RevKeen request failed (%d)\", response.StatusCode)) }\n  data, err := io.ReadAll(response.Body)\n  if err != nil { panic(err) }\n  fmt.Println(string(data))\n}"},{"lang":"php","label":"PHP (HTTP)","source":"<?php\n$request = curl_init(\"https://api.revkeen.com/v2/prices/11111111-1111-4111-8111-111111111111\");\ncurl_setopt_array($request, [\n  CURLOPT_CUSTOMREQUEST => \"PATCH\",\n  CURLOPT_RETURNTRANSFER => true,\n  CURLOPT_TIMEOUT => 10,\n  CURLOPT_HTTPHEADER => [\"x-api-key: \" . getenv(\"REVKEEN_API_KEY\"), \"Content-Type: application/json\"],\n  CURLOPT_POSTFIELDS => '{\\\"active\\\":true,\\\"name\\\":\\\"example\\\",\\\"nickname\\\":\\\"example\\\",\\\"description\\\":\\\"Example request from API docs\\\"}',\n]);\n$response = curl_exec($request);\nif ($response === false) throw new RuntimeException(curl_error($request));\n$status = curl_getinfo($request, CURLINFO_HTTP_CODE);\ncurl_close($request);\nif ($status >= 400) throw new RuntimeException(\"RevKeen request failed ($status)\");\necho $response;"},{"lang":"python","label":"Python","source":"import os\n\nimport requests\n\nresponse = requests.patch(\n    \"https://api.revkeen.com/v2/prices/11111111-1111-4111-8111-111111111111\",\n    headers={\"x-api-key\": os.environ[\"REVKEEN_API_KEY\"]},\n    json={\n        \"active\": True,\n        \"name\": \"example\",\n        \"nickname\": \"example\",\n        \"description\": \"Example request from API docs\",\n    },\n)\n\nresponse.raise_for_status()\nprint(response.json())"},{"lang":"javascript","label":"JavaScript","source":"const credential = process.env.REVKEEN_API_KEY;\nif (!credential) throw new Error(\"Set REVKEEN_API_KEY\");\nconst response = await fetch(\"https://api.revkeen.com/v2/prices/11111111-1111-4111-8111-111111111111\", {\n  method: \"PATCH\",\n  headers: {\n    \"x-api-key\": credential,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n    \"active\": true,\n    \"name\": \"example\",\n    \"nickname\": \"example\",\n    \"description\": \"Example request from API docs\"\n  }),\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"java","label":"Java","source":"import java.net.URI;\nimport java.net.http.HttpClient;\nimport java.net.http.HttpRequest;\nimport java.net.http.HttpResponse;\n\nHttpClient client = HttpClient.newHttpClient();\n\nHttpRequest request = HttpRequest.newBuilder()\n    .uri(URI.create(\"https://api.revkeen.com/v2/prices/11111111-1111-4111-8111-111111111111\"))\n    .header(\"x-api-key\", System.getenv(\"REVKEEN_API_KEY\"))\n    .header(\"Content-Type\", \"application/json\")\n    .method(\"PATCH\", HttpRequest.BodyPublishers.ofString(\"\"\"\n        {\n          \"active\": true,\n          \"name\": \"example\",\n          \"nickname\": \"example\",\n          \"description\": \"Example request from API docs\"\n        }\n        \"\"\"))\n    .build();\n\nHttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());\nSystem.out.println(response.body());"},{"lang":"ruby","label":"Ruby","source":"require \"net/http\"\nrequire \"json\"\nrequire \"uri\"\n\nuri = URI(\"https://api.revkeen.com/v2/prices/11111111-1111-4111-8111-111111111111\")\nhttp = Net::HTTP.new(uri.host, uri.port)\nhttp.use_ssl = true\n\nrequest = Net::HTTP::Patch.new(uri)\nrequest[\"x-api-key\"] = ENV[\"REVKEEN_API_KEY\"]\nrequest[\"Content-Type\"] = \"application/json\"\nrequest.body = {\n  \"active\" => true,\n  \"name\" => \"example\",\n  \"nickname\" => \"example\",\n  \"description\" => \"Example request from API docs\",\n}.to_json\n\nresponse = http.request(request)\nputs response.body"}],"security":[{"apiKey":["prices:write"]},{"oauth2":["prices:write"]}]},"delete":{"operationId":"prices_archive","tags":["Prices"],"x-fern-sdk-group-name":["prices"],"summary":"Archive a price","description":"Archives a price by setting active to false. The price remains in the system but cannot be used for new purchases.\n\n---\n\n**Related endpoints**\n\n- `GET /prices` — List prices\n- `POST /prices` — Create a price\n- `GET /prices/{id}` — Retrieve a price\n- `PATCH /prices/{id}` — Update a price\n\n**Common errors**\n\n- `401 authentication_error` — missing, invalid, expired, or revoked credential. Codes: `authentication_failed`, `invalid_api_key`, `expired_api_key`, `api_key_revoked`, `session_invalid`, `merchant_required`. Carries a `WWW-Authenticate: Bearer` challenge.\n- `404 resource_missing` — the referenced resource does not exist or is not visible to your key.\n\n**Idempotency**\n\nPass an `Idempotency-Key` header (UUID v4 recommended) to make retries safe. Keys are valid for 24 hours; see [the idempotency guide](/docs/fundamentals/idempotency).","parameters":[{"schema":{"type":"string","format":"uuid","description":"Price ID"},"required":true,"description":"Price ID","name":"id","in":"path"}],"responses":{"200":{"description":"Price archived successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Price"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PriceErrorResponse"}}},"headers":{"WWW-Authenticate":{"description":"RFC 6750 Bearer authentication challenge","schema":{"type":"string"},"example":"Bearer realm=\"revkeen-api\""}}},"404":{"description":"Price not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PriceErrorResponse"}}}},"429":{"description":"Too Many Requests - Rate limit exceeded","headers":{"Retry-After":{"description":"Whole seconds until the caller may retry","schema":{"type":"string"},"example":"60"},"X-RateLimit-Limit":{"description":"Request quota for the current window","schema":{"type":"string"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window","schema":{"type":"string"}},"X-RateLimit-Reset":{"description":"UNIX timestamp in seconds when the window resets","schema":{"type":"string"}}}}},"x-cli-group":"prices","x-cli-name":"archive","x-codeSamples":[{"lang":"curl","label":"cURL","source":"curl -X DELETE \"https://api.revkeen.com/v2/prices/11111111-1111-4111-8111-111111111111\" \\\n  -H \"x-api-key: $REVKEEN_API_KEY\""},{"lang":"typescript","label":"TypeScript (SDK)","source":"// npm install @revkeen/sdk@1.20260822.1541\nimport { RevKeenClient } from \"@revkeen/sdk\";\nconst apiKey = process.env.REVKEEN_API_KEY;\nif (!apiKey) throw new Error(\"Set REVKEEN_API_KEY\");\nconst client = new RevKeenClient({ apiKey, baseUrl: \"https://api.revkeen.com/v2\" });\nconst result = await client.prices.archive(\"11111111-1111-4111-8111-111111111111\");\nconsole.log(result);"},{"lang":"go","label":"Go (HTTP)","source":"package main\nimport (\"fmt\"; \"io\"; \"net/http\"; \"os\"; \"strings\"; \"time\")\nfunc main() {\n  req, err := http.NewRequest(\"DELETE\", \"https://api.revkeen.com/v2/prices/11111111-1111-4111-8111-111111111111\", strings.NewReader(\"\"))\n  if err != nil { panic(err) }\n  req.Header.Set(\"x-api-key\", os.Getenv(\"REVKEEN_API_KEY\"))\n  req.Header.Set(\"Content-Type\", \"application/json\")\n  client := &http.Client{Timeout: 10 * time.Second}\n  response, err := client.Do(req)\n  if err != nil { panic(err) }\n  defer response.Body.Close()\n  if response.StatusCode >= 400 { panic(fmt.Sprintf(\"RevKeen request failed (%d)\", response.StatusCode)) }\n  data, err := io.ReadAll(response.Body)\n  if err != nil { panic(err) }\n  fmt.Println(string(data))\n}"},{"lang":"php","label":"PHP (HTTP)","source":"<?php\n$request = curl_init(\"https://api.revkeen.com/v2/prices/11111111-1111-4111-8111-111111111111\");\ncurl_setopt_array($request, [\n  CURLOPT_CUSTOMREQUEST => \"DELETE\",\n  CURLOPT_RETURNTRANSFER => true,\n  CURLOPT_TIMEOUT => 10,\n  CURLOPT_HTTPHEADER => [\"x-api-key: \" . getenv(\"REVKEEN_API_KEY\"), \"Content-Type: application/json\"],\n]);\n$response = curl_exec($request);\nif ($response === false) throw new RuntimeException(curl_error($request));\n$status = curl_getinfo($request, CURLINFO_HTTP_CODE);\ncurl_close($request);\nif ($status >= 400) throw new RuntimeException(\"RevKeen request failed ($status)\");\necho $response;"},{"lang":"python","label":"Python","source":"import os\n\nimport requests\n\nresponse = requests.delete(\n    \"https://api.revkeen.com/v2/prices/11111111-1111-4111-8111-111111111111\",\n    headers={\"x-api-key\": os.environ[\"REVKEEN_API_KEY\"]},\n)\n\nresponse.raise_for_status()\nprint(response.json())"},{"lang":"javascript","label":"JavaScript","source":"const credential = process.env.REVKEEN_API_KEY;\nif (!credential) throw new Error(\"Set REVKEEN_API_KEY\");\nconst response = await fetch(\"https://api.revkeen.com/v2/prices/11111111-1111-4111-8111-111111111111\", {\n  method: \"DELETE\",\n  headers: {\n    \"x-api-key\": credential,\n  },\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"java","label":"Java","source":"import java.net.URI;\nimport java.net.http.HttpClient;\nimport java.net.http.HttpRequest;\nimport java.net.http.HttpResponse;\n\nHttpClient client = HttpClient.newHttpClient();\n\nHttpRequest request = HttpRequest.newBuilder()\n    .uri(URI.create(\"https://api.revkeen.com/v2/prices/11111111-1111-4111-8111-111111111111\"))\n    .header(\"x-api-key\", System.getenv(\"REVKEEN_API_KEY\"))\n    .method(\"DELETE\", HttpRequest.BodyPublishers.noBody())\n    .build();\n\nHttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());\nSystem.out.println(response.body());"},{"lang":"ruby","label":"Ruby","source":"require \"net/http\"\nrequire \"json\"\nrequire \"uri\"\n\nuri = URI(\"https://api.revkeen.com/v2/prices/11111111-1111-4111-8111-111111111111\")\nhttp = Net::HTTP.new(uri.host, uri.port)\nhttp.use_ssl = true\n\nrequest = Net::HTTP::Delete.new(uri)\nrequest[\"x-api-key\"] = ENV[\"REVKEEN_API_KEY\"]\n\nresponse = http.request(request)\nputs response.body"}],"security":[{"apiKey":["prices:write"]},{"oauth2":["prices:write"]}]}},"/meters":{"get":{"operationId":"meters_list","tags":["Meters"],"x-fern-sdk-group-name":["meters"],"x-fern-sdk-method-name":"list","summary":"List meters","description":"List all usage meters with optional filtering and pagination.\n\n---\n\n**Related endpoints**\n\n- `POST /meters` — Create a meter\n- `GET /meters/{id}/quantities` — Get meter quantities\n- `GET /meters/{id}` — Get a meter\n- `PATCH /meters/{id}` — Update a meter\n\n**Common errors**\n\n- `401 authentication_error` — missing, invalid, expired, or revoked credential. Codes: `authentication_failed`, `invalid_api_key`, `expired_api_key`, `api_key_revoked`, `session_invalid`, `merchant_required`. Carries a `WWW-Authenticate: Bearer` challenge.\n\n**Pagination**\n\nOffset-based with `limit` (default 25, max 100) and `offset`. The response `pagination` block includes `total` and `hasMore`. See [the pagination guide](/docs/fundamentals/pagination) for SDK auto-paging helpers.","security":[{"apiKey":["meters:read"]},{"apiKey":["prices:read"]}],"parameters":[{"schema":{"type":"string","description":"Include archived meters","example":"false"},"required":false,"description":"Include archived meters","name":"include_archived","in":"query"},{"schema":{"type":"string","description":"Max results (default 50, max 100)","example":"50"},"required":false,"description":"Max results (default 50, max 100)","name":"limit","in":"query"},{"schema":{"type":"string","description":"Pagination offset","example":"0"},"required":false,"description":"Pagination offset","name":"offset","in":"query"}],"responses":{"200":{"description":"List of meters","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MeterListResponse"}}}},"401":{"description":"Unauthorized - Invalid or missing API key","headers":{"WWW-Authenticate":{"schema":{"type":"string","description":"RFC 6750 Bearer authentication challenge","example":"Bearer realm=\"revkeen-api\""},"required":true,"description":"RFC 6750 Bearer authentication challenge"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthenticationError"}}}},"429":{"description":"Too Many Requests - Rate limit exceeded","headers":{"Retry-After":{"description":"Whole seconds until the caller may retry","schema":{"type":"string"},"example":"60"},"X-RateLimit-Limit":{"description":"Request quota for the current window","schema":{"type":"string"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window","schema":{"type":"string"}},"X-RateLimit-Reset":{"description":"UNIX timestamp in seconds when the window resets","schema":{"type":"string"}}}}},"x-cli-group":"meters","x-cli-name":"list","x-codeSamples":[{"lang":"curl","label":"cURL","source":"curl -X GET \"https://api.revkeen.com/v2/meters?include_archived=false&limit=50\" \\\n  -H \"x-api-key: $REVKEEN_API_KEY\""},{"lang":"typescript","label":"TypeScript (HTTP)","source":"const credential = process.env.REVKEEN_API_KEY;\nif (!credential) throw new Error(\"Set REVKEEN_API_KEY\");\nconst response = await fetch(\"https://api.revkeen.com/v2/meters?include_archived=false&limit=50\", {\n  method: \"GET\",\n  headers: {\n    \"x-api-key\": credential,\n  },\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"go","label":"Go (HTTP)","source":"package main\nimport (\"fmt\"; \"io\"; \"net/http\"; \"os\"; \"strings\"; \"time\")\nfunc main() {\n  req, err := http.NewRequest(\"GET\", \"https://api.revkeen.com/v2/meters?include_archived=false&limit=50\", strings.NewReader(\"\"))\n  if err != nil { panic(err) }\n  req.Header.Set(\"x-api-key\", os.Getenv(\"REVKEEN_API_KEY\"))\n  req.Header.Set(\"Content-Type\", \"application/json\")\n  client := &http.Client{Timeout: 10 * time.Second}\n  response, err := client.Do(req)\n  if err != nil { panic(err) }\n  defer response.Body.Close()\n  if response.StatusCode >= 400 { panic(fmt.Sprintf(\"RevKeen request failed (%d)\", response.StatusCode)) }\n  data, err := io.ReadAll(response.Body)\n  if err != nil { panic(err) }\n  fmt.Println(string(data))\n}"},{"lang":"php","label":"PHP (HTTP)","source":"<?php\n$request = curl_init(\"https://api.revkeen.com/v2/meters?include_archived=false&limit=50\");\ncurl_setopt_array($request, [\n  CURLOPT_CUSTOMREQUEST => \"GET\",\n  CURLOPT_RETURNTRANSFER => true,\n  CURLOPT_TIMEOUT => 10,\n  CURLOPT_HTTPHEADER => [\"x-api-key: \" . getenv(\"REVKEEN_API_KEY\"), \"Content-Type: application/json\"],\n]);\n$response = curl_exec($request);\nif ($response === false) throw new RuntimeException(curl_error($request));\n$status = curl_getinfo($request, CURLINFO_HTTP_CODE);\ncurl_close($request);\nif ($status >= 400) throw new RuntimeException(\"RevKeen request failed ($status)\");\necho $response;"},{"lang":"python","label":"Python","source":"import os\n\nimport requests\n\nresponse = requests.get(\n    \"https://api.revkeen.com/v2/meters\",\n    headers={\"x-api-key\": os.environ[\"REVKEEN_API_KEY\"]},\n    params={\n        \"include_archived\": \"false\",\n        \"limit\": \"50\",\n    },\n)\n\nresponse.raise_for_status()\nprint(response.json())"},{"lang":"javascript","label":"JavaScript","source":"const credential = process.env.REVKEEN_API_KEY;\nif (!credential) throw new Error(\"Set REVKEEN_API_KEY\");\nconst response = await fetch(\"https://api.revkeen.com/v2/meters?include_archived=false&limit=50\", {\n  method: \"GET\",\n  headers: {\n    \"x-api-key\": credential,\n  },\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"java","label":"Java","source":"import java.net.URI;\nimport java.net.http.HttpClient;\nimport java.net.http.HttpRequest;\nimport java.net.http.HttpResponse;\n\nHttpClient client = HttpClient.newHttpClient();\n\nHttpRequest request = HttpRequest.newBuilder()\n    .uri(URI.create(\"https://api.revkeen.com/v2/meters?include_archived=false&limit=50\"))\n    .header(\"x-api-key\", System.getenv(\"REVKEEN_API_KEY\"))\n    .GET()\n    .build();\n\nHttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());\nSystem.out.println(response.body());"},{"lang":"ruby","label":"Ruby","source":"require \"net/http\"\nrequire \"json\"\nrequire \"uri\"\n\nuri = URI(\"https://api.revkeen.com/v2/meters?include_archived=false&limit=50\")\nhttp = Net::HTTP.new(uri.host, uri.port)\nhttp.use_ssl = true\n\nrequest = Net::HTTP::Get.new(uri)\nrequest[\"x-api-key\"] = ENV[\"REVKEEN_API_KEY\"]\n\nresponse = http.request(request)\nputs response.body"}]},"post":{"operationId":"meters_create","tags":["Meters"],"x-fern-sdk-group-name":["meters"],"x-fern-sdk-method-name":"create","summary":"Create a meter","description":"Create a new usage meter. Meters define how usage events are aggregated for billing.\n\n---\n\n**Related endpoints**\n\n- `GET /meters` — List meters\n- `GET /meters/{id}/quantities` — Get meter quantities\n- `GET /meters/{id}` — Get a meter\n- `PATCH /meters/{id}` — Update a meter\n\n**Common errors**\n\n- `400 invalid_request` — malformed payload or failed validation.\n- `401 authentication_error` — missing, invalid, expired, or revoked credential. Codes: `authentication_failed`, `invalid_api_key`, `expired_api_key`, `api_key_revoked`, `session_invalid`, `merchant_required`. Carries a `WWW-Authenticate: Bearer` challenge.\n\n**Idempotency**\n\nPass an `Idempotency-Key` header (UUID v4 recommended) to make retries safe. Keys are valid for 24 hours; see [the idempotency guide](/docs/fundamentals/idempotency).","security":[{"apiKey":["meters:write"]},{"apiKey":["prices:write"]}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateMeterRequest"}}}},"responses":{"201":{"description":"Meter created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateMeterResponse"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MeterError"}}}},"401":{"description":"Unauthorized - Invalid or missing API key","headers":{"WWW-Authenticate":{"schema":{"type":"string","description":"RFC 6750 Bearer authentication challenge","example":"Bearer realm=\"revkeen-api\""},"required":true,"description":"RFC 6750 Bearer authentication challenge"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthenticationError"}}}},"429":{"description":"Too Many Requests - Rate limit exceeded","headers":{"Retry-After":{"description":"Whole seconds until the caller may retry","schema":{"type":"string"},"example":"60"},"X-RateLimit-Limit":{"description":"Request quota for the current window","schema":{"type":"string"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window","schema":{"type":"string"}},"X-RateLimit-Reset":{"description":"UNIX timestamp in seconds when the window resets","schema":{"type":"string"}}}}},"x-cli-group":"meters","x-cli-name":"create","x-codeSamples":[{"lang":"curl","label":"cURL","source":"curl -X POST \"https://api.revkeen.com/v2/meters\" \\\n  -H \"x-api-key: $REVKEEN_API_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"name\": \"API Calls\",\n    \"event_name\": \"api_call\"\n  }'"},{"lang":"typescript","label":"TypeScript (HTTP)","source":"const credential = process.env.REVKEEN_API_KEY;\nif (!credential) throw new Error(\"Set REVKEEN_API_KEY\");\nconst response = await fetch(\"https://api.revkeen.com/v2/meters\", {\n  method: \"POST\",\n  headers: {\n    \"x-api-key\": credential,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n    \"name\": \"API Calls\",\n    \"event_name\": \"api_call\"\n  }),\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"go","label":"Go (HTTP)","source":"package main\nimport (\"fmt\"; \"io\"; \"net/http\"; \"os\"; \"strings\"; \"time\")\nfunc main() {\n  req, err := http.NewRequest(\"POST\", \"https://api.revkeen.com/v2/meters\", strings.NewReader(\"{\\\"name\\\":\\\"API Calls\\\",\\\"event_name\\\":\\\"api_call\\\"}\"))\n  if err != nil { panic(err) }\n  req.Header.Set(\"x-api-key\", os.Getenv(\"REVKEEN_API_KEY\"))\n  req.Header.Set(\"Content-Type\", \"application/json\")\n  client := &http.Client{Timeout: 10 * time.Second}\n  response, err := client.Do(req)\n  if err != nil { panic(err) }\n  defer response.Body.Close()\n  if response.StatusCode >= 400 { panic(fmt.Sprintf(\"RevKeen request failed (%d)\", response.StatusCode)) }\n  data, err := io.ReadAll(response.Body)\n  if err != nil { panic(err) }\n  fmt.Println(string(data))\n}"},{"lang":"php","label":"PHP (HTTP)","source":"<?php\n$request = curl_init(\"https://api.revkeen.com/v2/meters\");\ncurl_setopt_array($request, [\n  CURLOPT_CUSTOMREQUEST => \"POST\",\n  CURLOPT_RETURNTRANSFER => true,\n  CURLOPT_TIMEOUT => 10,\n  CURLOPT_HTTPHEADER => [\"x-api-key: \" . getenv(\"REVKEEN_API_KEY\"), \"Content-Type: application/json\"],\n  CURLOPT_POSTFIELDS => '{\\\"name\\\":\\\"API Calls\\\",\\\"event_name\\\":\\\"api_call\\\"}',\n]);\n$response = curl_exec($request);\nif ($response === false) throw new RuntimeException(curl_error($request));\n$status = curl_getinfo($request, CURLINFO_HTTP_CODE);\ncurl_close($request);\nif ($status >= 400) throw new RuntimeException(\"RevKeen request failed ($status)\");\necho $response;"},{"lang":"python","label":"Python","source":"import os\n\nimport requests\n\nresponse = requests.post(\n    \"https://api.revkeen.com/v2/meters\",\n    headers={\"x-api-key\": os.environ[\"REVKEEN_API_KEY\"]},\n    json={\n        \"name\": \"API Calls\",\n        \"event_name\": \"api_call\",\n    },\n)\n\nresponse.raise_for_status()\nprint(response.json())"},{"lang":"javascript","label":"JavaScript","source":"const credential = process.env.REVKEEN_API_KEY;\nif (!credential) throw new Error(\"Set REVKEEN_API_KEY\");\nconst response = await fetch(\"https://api.revkeen.com/v2/meters\", {\n  method: \"POST\",\n  headers: {\n    \"x-api-key\": credential,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n    \"name\": \"API Calls\",\n    \"event_name\": \"api_call\"\n  }),\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"java","label":"Java","source":"import java.net.URI;\nimport java.net.http.HttpClient;\nimport java.net.http.HttpRequest;\nimport java.net.http.HttpResponse;\n\nHttpClient client = HttpClient.newHttpClient();\n\nHttpRequest request = HttpRequest.newBuilder()\n    .uri(URI.create(\"https://api.revkeen.com/v2/meters\"))\n    .header(\"x-api-key\", System.getenv(\"REVKEEN_API_KEY\"))\n    .header(\"Content-Type\", \"application/json\")\n    .method(\"POST\", HttpRequest.BodyPublishers.ofString(\"\"\"\n        {\n          \"name\": \"API Calls\",\n          \"event_name\": \"api_call\"\n        }\n        \"\"\"))\n    .build();\n\nHttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());\nSystem.out.println(response.body());"},{"lang":"ruby","label":"Ruby","source":"require \"net/http\"\nrequire \"json\"\nrequire \"uri\"\n\nuri = URI(\"https://api.revkeen.com/v2/meters\")\nhttp = Net::HTTP.new(uri.host, uri.port)\nhttp.use_ssl = true\n\nrequest = Net::HTTP::Post.new(uri)\nrequest[\"x-api-key\"] = ENV[\"REVKEEN_API_KEY\"]\nrequest[\"Content-Type\"] = \"application/json\"\nrequest.body = {\n  \"name\" => \"API Calls\",\n  \"event_name\" => \"api_call\",\n}.to_json\n\nresponse = http.request(request)\nputs response.body"}]}},"/meters/{id}/quantities":{"get":{"operationId":"meters_quantities","tags":["Meters"],"x-fern-sdk-group-name":["meters"],"x-fern-sdk-method-name":"quantities","summary":"Get meter quantities","description":"Get bucketed quantities for a meter over a time period. Timestamps are UTC. Polar-compatible query names (`start_timestamp`, `end_timestamp`, `interval`) are required.\n\n---\n\n**Related endpoints**\n\n- `GET /meters` — List meters\n- `POST /meters` — Create a meter\n- `GET /meters/{id}` — Get a meter\n- `PATCH /meters/{id}` — Update a meter\n\n**Common errors**\n\n- `400 invalid_request` — malformed payload or failed validation.\n- `401 authentication_error` — missing, invalid, expired, or revoked credential. Codes: `authentication_failed`, `invalid_api_key`, `expired_api_key`, `api_key_revoked`, `session_invalid`, `merchant_required`. Carries a `WWW-Authenticate: Bearer` challenge.\n- `404 resource_missing` — the referenced resource does not exist or is not visible to your key.","security":[{"apiKey":["meters:read"]},{"apiKey":["prices:read"]}],"parameters":[{"schema":{"type":"string","format":"uuid","description":"Meter UUID.","example":"e3e70682-c209-1cac-a29f-6fbed82c07cd"},"required":true,"description":"Meter UUID.","name":"id","in":"path"},{"schema":{"type":"string","format":"date-time","description":"Start timestamp (ISO 8601, UTC)","example":"2026-03-01T00:00:00Z"},"required":true,"description":"Start timestamp (ISO 8601, UTC)","name":"start_timestamp","in":"query"},{"schema":{"type":"string","format":"date-time","description":"End timestamp (ISO 8601, UTC)","example":"2026-03-08T00:00:00Z"},"required":true,"description":"End timestamp (ISO 8601, UTC)","name":"end_timestamp","in":"query"},{"schema":{"type":"string","enum":["hour","day","week","month","year"],"description":"Bucket width. UTC date_trunc semantics; week is ISO (Monday).","example":"day"},"required":true,"description":"Bucket width. UTC date_trunc semantics; week is ISO (Monday).","name":"interval","in":"query"},{"schema":{"type":"string","format":"uuid","description":"Filter by customer UUID"},"required":false,"description":"Filter by customer UUID","name":"customer_id","in":"query"},{"schema":{"type":"string","description":"Filter by the merchant's external customer ID"},"required":false,"description":"Filter by the merchant's external customer ID","name":"external_customer_id","in":"query"},{"schema":{"type":"string","format":"uuid","description":"Filter by subscription UUID"},"required":false,"description":"Filter by subscription UUID","name":"subscription_id","in":"query"},{"schema":{"type":"string","description":"Only UTC is supported. Omit or pass UTC.","example":"UTC"},"required":false,"description":"Only UTC is supported. Omit or pass UTC.","name":"timezone","in":"query"}],"responses":{"200":{"description":"Meter quantities","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MeterQuantitiesResponse"}}}},"400":{"description":"A parameter failed validation (`param` names it), or the requested range is missing or invalid","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MeterQuantitiesValidationError"}}}},"401":{"description":"Unauthorized - Invalid or missing API key","headers":{"WWW-Authenticate":{"schema":{"type":"string","description":"RFC 6750 Bearer authentication challenge","example":"Bearer realm=\"revkeen-api\""},"required":true,"description":"RFC 6750 Bearer authentication challenge"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthenticationError"}}}},"404":{"description":"Meter not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MeterError"}}}},"429":{"description":"Too Many Requests - Rate limit exceeded","headers":{"Retry-After":{"description":"Whole seconds until the caller may retry","schema":{"type":"string"},"example":"60"},"X-RateLimit-Limit":{"description":"Request quota for the current window","schema":{"type":"string"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window","schema":{"type":"string"}},"X-RateLimit-Reset":{"description":"UNIX timestamp in seconds when the window resets","schema":{"type":"string"}}}}},"x-cli-group":"meters","x-cli-name":"quantities","x-codeSamples":[{"lang":"curl","label":"cURL","source":"curl -X GET \"https://api.revkeen.com/v2/meters/11111111-1111-4111-8111-111111111111/quantities?start_timestamp=2026-03-01T00%3A00%3A00Z&end_timestamp=2026-03-08T00%3A00%3A00Z&interval=day&customer_id=11111111-1111-4111-8111-111111111111\" \\\n  -H \"x-api-key: $REVKEEN_API_KEY\""},{"lang":"typescript","label":"TypeScript (HTTP)","source":"const credential = process.env.REVKEEN_API_KEY;\nif (!credential) throw new Error(\"Set REVKEEN_API_KEY\");\nconst response = await fetch(\"https://api.revkeen.com/v2/meters/11111111-1111-4111-8111-111111111111/quantities?start_timestamp=2026-03-01T00%3A00%3A00Z&end_timestamp=2026-03-08T00%3A00%3A00Z&interval=day&customer_id=11111111-1111-4111-8111-111111111111\", {\n  method: \"GET\",\n  headers: {\n    \"x-api-key\": credential,\n  },\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"go","label":"Go (HTTP)","source":"package main\nimport (\"fmt\"; \"io\"; \"net/http\"; \"os\"; \"strings\"; \"time\")\nfunc main() {\n  req, err := http.NewRequest(\"GET\", \"https://api.revkeen.com/v2/meters/11111111-1111-4111-8111-111111111111/quantities?start_timestamp=2026-03-01T00%3A00%3A00Z&end_timestamp=2026-03-08T00%3A00%3A00Z&interval=day&customer_id=11111111-1111-4111-8111-111111111111\", strings.NewReader(\"\"))\n  if err != nil { panic(err) }\n  req.Header.Set(\"x-api-key\", os.Getenv(\"REVKEEN_API_KEY\"))\n  req.Header.Set(\"Content-Type\", \"application/json\")\n  client := &http.Client{Timeout: 10 * time.Second}\n  response, err := client.Do(req)\n  if err != nil { panic(err) }\n  defer response.Body.Close()\n  if response.StatusCode >= 400 { panic(fmt.Sprintf(\"RevKeen request failed (%d)\", response.StatusCode)) }\n  data, err := io.ReadAll(response.Body)\n  if err != nil { panic(err) }\n  fmt.Println(string(data))\n}"},{"lang":"php","label":"PHP (HTTP)","source":"<?php\n$request = curl_init(\"https://api.revkeen.com/v2/meters/11111111-1111-4111-8111-111111111111/quantities?start_timestamp=2026-03-01T00%3A00%3A00Z&end_timestamp=2026-03-08T00%3A00%3A00Z&interval=day&customer_id=11111111-1111-4111-8111-111111111111\");\ncurl_setopt_array($request, [\n  CURLOPT_CUSTOMREQUEST => \"GET\",\n  CURLOPT_RETURNTRANSFER => true,\n  CURLOPT_TIMEOUT => 10,\n  CURLOPT_HTTPHEADER => [\"x-api-key: \" . getenv(\"REVKEEN_API_KEY\"), \"Content-Type: application/json\"],\n]);\n$response = curl_exec($request);\nif ($response === false) throw new RuntimeException(curl_error($request));\n$status = curl_getinfo($request, CURLINFO_HTTP_CODE);\ncurl_close($request);\nif ($status >= 400) throw new RuntimeException(\"RevKeen request failed ($status)\");\necho $response;"},{"lang":"python","label":"Python","source":"import os\n\nimport requests\n\nresponse = requests.get(\n    \"https://api.revkeen.com/v2/meters/11111111-1111-4111-8111-111111111111/quantities\",\n    headers={\"x-api-key\": os.environ[\"REVKEEN_API_KEY\"]},\n    params={\n        \"start_timestamp\": \"2026-03-01T00:00:00Z\",\n        \"end_timestamp\": \"2026-03-08T00:00:00Z\",\n        \"interval\": \"day\",\n        \"customer_id\": \"11111111-1111-4111-8111-111111111111\",\n    },\n)\n\nresponse.raise_for_status()\nprint(response.json())"},{"lang":"javascript","label":"JavaScript","source":"const credential = process.env.REVKEEN_API_KEY;\nif (!credential) throw new Error(\"Set REVKEEN_API_KEY\");\nconst response = await fetch(\"https://api.revkeen.com/v2/meters/11111111-1111-4111-8111-111111111111/quantities?start_timestamp=2026-03-01T00%3A00%3A00Z&end_timestamp=2026-03-08T00%3A00%3A00Z&interval=day&customer_id=11111111-1111-4111-8111-111111111111\", {\n  method: \"GET\",\n  headers: {\n    \"x-api-key\": credential,\n  },\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"java","label":"Java","source":"import java.net.URI;\nimport java.net.http.HttpClient;\nimport java.net.http.HttpRequest;\nimport java.net.http.HttpResponse;\n\nHttpClient client = HttpClient.newHttpClient();\n\nHttpRequest request = HttpRequest.newBuilder()\n    .uri(URI.create(\"https://api.revkeen.com/v2/meters/11111111-1111-4111-8111-111111111111/quantities?start_timestamp=2026-03-01T00%3A00%3A00Z&end_timestamp=2026-03-08T00%3A00%3A00Z&interval=day&customer_id=11111111-1111-4111-8111-111111111111\"))\n    .header(\"x-api-key\", System.getenv(\"REVKEEN_API_KEY\"))\n    .GET()\n    .build();\n\nHttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());\nSystem.out.println(response.body());"},{"lang":"ruby","label":"Ruby","source":"require \"net/http\"\nrequire \"json\"\nrequire \"uri\"\n\nuri = URI(\"https://api.revkeen.com/v2/meters/11111111-1111-4111-8111-111111111111/quantities?start_timestamp=2026-03-01T00%3A00%3A00Z&end_timestamp=2026-03-08T00%3A00%3A00Z&interval=day&customer_id=11111111-1111-4111-8111-111111111111\")\nhttp = Net::HTTP.new(uri.host, uri.port)\nhttp.use_ssl = true\n\nrequest = Net::HTTP::Get.new(uri)\nrequest[\"x-api-key\"] = ENV[\"REVKEEN_API_KEY\"]\n\nresponse = http.request(request)\nputs response.body"}]}},"/meters/{id}":{"get":{"operationId":"meters_get","tags":["Meters"],"x-fern-sdk-group-name":["meters"],"x-fern-sdk-method-name":"get","summary":"Get a meter","description":"Retrieve a single meter by ID.\n\n---\n\n**Related endpoints**\n\n- `GET /meters` — List meters\n- `POST /meters` — Create a meter\n- `GET /meters/{id}/quantities` — Get meter quantities\n- `PATCH /meters/{id}` — Update a meter\n\n**Common errors**\n\n- `401 authentication_error` — missing, invalid, expired, or revoked credential. Codes: `authentication_failed`, `invalid_api_key`, `expired_api_key`, `api_key_revoked`, `session_invalid`, `merchant_required`. Carries a `WWW-Authenticate: Bearer` challenge.\n- `404 resource_missing` — the referenced resource does not exist or is not visible to your key.","security":[{"apiKey":["meters:read"]},{"apiKey":["prices:read"]}],"parameters":[{"schema":{"type":"string","format":"uuid","description":"Meter UUID.","example":"e3e70682-c209-1cac-a29f-6fbed82c07cd"},"required":true,"description":"Meter UUID.","name":"id","in":"path"}],"responses":{"200":{"description":"Meter details","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MeterGetResponse"}}}},"401":{"description":"Unauthorized - Invalid or missing API key","headers":{"WWW-Authenticate":{"schema":{"type":"string","description":"RFC 6750 Bearer authentication challenge","example":"Bearer realm=\"revkeen-api\""},"required":true,"description":"RFC 6750 Bearer authentication challenge"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthenticationError"}}}},"404":{"description":"Meter not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MeterError"}}}},"429":{"description":"Too Many Requests - Rate limit exceeded","headers":{"Retry-After":{"description":"Whole seconds until the caller may retry","schema":{"type":"string"},"example":"60"},"X-RateLimit-Limit":{"description":"Request quota for the current window","schema":{"type":"string"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window","schema":{"type":"string"}},"X-RateLimit-Reset":{"description":"UNIX timestamp in seconds when the window resets","schema":{"type":"string"}}}}},"x-cli-group":"meters","x-cli-name":"get","x-codeSamples":[{"lang":"curl","label":"cURL","source":"curl -X GET \"https://api.revkeen.com/v2/meters/11111111-1111-4111-8111-111111111111\" \\\n  -H \"x-api-key: $REVKEEN_API_KEY\""},{"lang":"typescript","label":"TypeScript (HTTP)","source":"const credential = process.env.REVKEEN_API_KEY;\nif (!credential) throw new Error(\"Set REVKEEN_API_KEY\");\nconst response = await fetch(\"https://api.revkeen.com/v2/meters/11111111-1111-4111-8111-111111111111\", {\n  method: \"GET\",\n  headers: {\n    \"x-api-key\": credential,\n  },\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"go","label":"Go (HTTP)","source":"package main\nimport (\"fmt\"; \"io\"; \"net/http\"; \"os\"; \"strings\"; \"time\")\nfunc main() {\n  req, err := http.NewRequest(\"GET\", \"https://api.revkeen.com/v2/meters/11111111-1111-4111-8111-111111111111\", strings.NewReader(\"\"))\n  if err != nil { panic(err) }\n  req.Header.Set(\"x-api-key\", os.Getenv(\"REVKEEN_API_KEY\"))\n  req.Header.Set(\"Content-Type\", \"application/json\")\n  client := &http.Client{Timeout: 10 * time.Second}\n  response, err := client.Do(req)\n  if err != nil { panic(err) }\n  defer response.Body.Close()\n  if response.StatusCode >= 400 { panic(fmt.Sprintf(\"RevKeen request failed (%d)\", response.StatusCode)) }\n  data, err := io.ReadAll(response.Body)\n  if err != nil { panic(err) }\n  fmt.Println(string(data))\n}"},{"lang":"php","label":"PHP (HTTP)","source":"<?php\n$request = curl_init(\"https://api.revkeen.com/v2/meters/11111111-1111-4111-8111-111111111111\");\ncurl_setopt_array($request, [\n  CURLOPT_CUSTOMREQUEST => \"GET\",\n  CURLOPT_RETURNTRANSFER => true,\n  CURLOPT_TIMEOUT => 10,\n  CURLOPT_HTTPHEADER => [\"x-api-key: \" . getenv(\"REVKEEN_API_KEY\"), \"Content-Type: application/json\"],\n]);\n$response = curl_exec($request);\nif ($response === false) throw new RuntimeException(curl_error($request));\n$status = curl_getinfo($request, CURLINFO_HTTP_CODE);\ncurl_close($request);\nif ($status >= 400) throw new RuntimeException(\"RevKeen request failed ($status)\");\necho $response;"},{"lang":"python","label":"Python","source":"import os\n\nimport requests\n\nresponse = requests.get(\n    \"https://api.revkeen.com/v2/meters/11111111-1111-4111-8111-111111111111\",\n    headers={\"x-api-key\": os.environ[\"REVKEEN_API_KEY\"]},\n)\n\nresponse.raise_for_status()\nprint(response.json())"},{"lang":"javascript","label":"JavaScript","source":"const credential = process.env.REVKEEN_API_KEY;\nif (!credential) throw new Error(\"Set REVKEEN_API_KEY\");\nconst response = await fetch(\"https://api.revkeen.com/v2/meters/11111111-1111-4111-8111-111111111111\", {\n  method: \"GET\",\n  headers: {\n    \"x-api-key\": credential,\n  },\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"java","label":"Java","source":"import java.net.URI;\nimport java.net.http.HttpClient;\nimport java.net.http.HttpRequest;\nimport java.net.http.HttpResponse;\n\nHttpClient client = HttpClient.newHttpClient();\n\nHttpRequest request = HttpRequest.newBuilder()\n    .uri(URI.create(\"https://api.revkeen.com/v2/meters/11111111-1111-4111-8111-111111111111\"))\n    .header(\"x-api-key\", System.getenv(\"REVKEEN_API_KEY\"))\n    .GET()\n    .build();\n\nHttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());\nSystem.out.println(response.body());"},{"lang":"ruby","label":"Ruby","source":"require \"net/http\"\nrequire \"json\"\nrequire \"uri\"\n\nuri = URI(\"https://api.revkeen.com/v2/meters/11111111-1111-4111-8111-111111111111\")\nhttp = Net::HTTP.new(uri.host, uri.port)\nhttp.use_ssl = true\n\nrequest = Net::HTTP::Get.new(uri)\nrequest[\"x-api-key\"] = ENV[\"REVKEEN_API_KEY\"]\n\nresponse = http.request(request)\nputs response.body"}]},"patch":{"operationId":"meters_update","tags":["Meters"],"x-fern-sdk-group-name":["meters"],"x-fern-sdk-method-name":"update","summary":"Update a meter","description":"Update a meter's configuration. Note: `event_name` and `aggregation` are immutable after creation.\n\n---\n\n**Related endpoints**\n\n- `GET /meters` — List meters\n- `POST /meters` — Create a meter\n- `GET /meters/{id}/quantities` — Get meter quantities\n- `GET /meters/{id}` — Get a meter\n\n**Common errors**\n\n- `400 invalid_request` — malformed payload or failed validation.\n- `401 authentication_error` — missing, invalid, expired, or revoked credential. Codes: `authentication_failed`, `invalid_api_key`, `expired_api_key`, `api_key_revoked`, `session_invalid`, `merchant_required`. Carries a `WWW-Authenticate: Bearer` challenge.\n\n**Idempotency**\n\nPass an `Idempotency-Key` header (UUID v4 recommended) to make retries safe. Keys are valid for 24 hours; see [the idempotency guide](/docs/fundamentals/idempotency).","security":[{"apiKey":["meters:write"]},{"apiKey":["prices:write"]}],"parameters":[{"schema":{"type":"string","format":"uuid","description":"Meter UUID.","example":"e3e70682-c209-1cac-a29f-6fbed82c07cd"},"required":true,"description":"Meter UUID.","name":"id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateMeterRequest"}}}},"responses":{"200":{"description":"Meter updated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateMeterResponse"}}}},"400":{"description":"Validation error or immutable field changed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MeterError"}}}},"401":{"description":"Unauthorized - Invalid or missing API key","headers":{"WWW-Authenticate":{"schema":{"type":"string","description":"RFC 6750 Bearer authentication challenge","example":"Bearer realm=\"revkeen-api\""},"required":true,"description":"RFC 6750 Bearer authentication challenge"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthenticationError"}}}},"429":{"description":"Too Many Requests - Rate limit exceeded","headers":{"Retry-After":{"description":"Whole seconds until the caller may retry","schema":{"type":"string"},"example":"60"},"X-RateLimit-Limit":{"description":"Request quota for the current window","schema":{"type":"string"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window","schema":{"type":"string"}},"X-RateLimit-Reset":{"description":"UNIX timestamp in seconds when the window resets","schema":{"type":"string"}}}}},"x-cli-group":"meters","x-cli-name":"update","x-codeSamples":[{"lang":"curl","label":"cURL","source":"curl -X PATCH \"https://api.revkeen.com/v2/meters/11111111-1111-4111-8111-111111111111\" \\\n  -H \"x-api-key: $REVKEEN_API_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"name\": \"example\",\n    \"slug\": \"example\",\n    \"description\": \"Example request from API docs\",\n    \"status\": \"active\"\n  }'"},{"lang":"typescript","label":"TypeScript (HTTP)","source":"const credential = process.env.REVKEEN_API_KEY;\nif (!credential) throw new Error(\"Set REVKEEN_API_KEY\");\nconst response = await fetch(\"https://api.revkeen.com/v2/meters/11111111-1111-4111-8111-111111111111\", {\n  method: \"PATCH\",\n  headers: {\n    \"x-api-key\": credential,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n    \"name\": \"example\",\n    \"slug\": \"example\",\n    \"description\": \"Example request from API docs\",\n    \"status\": \"active\"\n  }),\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"go","label":"Go (HTTP)","source":"package main\nimport (\"fmt\"; \"io\"; \"net/http\"; \"os\"; \"strings\"; \"time\")\nfunc main() {\n  req, err := http.NewRequest(\"PATCH\", \"https://api.revkeen.com/v2/meters/11111111-1111-4111-8111-111111111111\", strings.NewReader(\"{\\\"name\\\":\\\"example\\\",\\\"slug\\\":\\\"example\\\",\\\"description\\\":\\\"Example request from API docs\\\",\\\"status\\\":\\\"active\\\"}\"))\n  if err != nil { panic(err) }\n  req.Header.Set(\"x-api-key\", os.Getenv(\"REVKEEN_API_KEY\"))\n  req.Header.Set(\"Content-Type\", \"application/json\")\n  client := &http.Client{Timeout: 10 * time.Second}\n  response, err := client.Do(req)\n  if err != nil { panic(err) }\n  defer response.Body.Close()\n  if response.StatusCode >= 400 { panic(fmt.Sprintf(\"RevKeen request failed (%d)\", response.StatusCode)) }\n  data, err := io.ReadAll(response.Body)\n  if err != nil { panic(err) }\n  fmt.Println(string(data))\n}"},{"lang":"php","label":"PHP (HTTP)","source":"<?php\n$request = curl_init(\"https://api.revkeen.com/v2/meters/11111111-1111-4111-8111-111111111111\");\ncurl_setopt_array($request, [\n  CURLOPT_CUSTOMREQUEST => \"PATCH\",\n  CURLOPT_RETURNTRANSFER => true,\n  CURLOPT_TIMEOUT => 10,\n  CURLOPT_HTTPHEADER => [\"x-api-key: \" . getenv(\"REVKEEN_API_KEY\"), \"Content-Type: application/json\"],\n  CURLOPT_POSTFIELDS => '{\\\"name\\\":\\\"example\\\",\\\"slug\\\":\\\"example\\\",\\\"description\\\":\\\"Example request from API docs\\\",\\\"status\\\":\\\"active\\\"}',\n]);\n$response = curl_exec($request);\nif ($response === false) throw new RuntimeException(curl_error($request));\n$status = curl_getinfo($request, CURLINFO_HTTP_CODE);\ncurl_close($request);\nif ($status >= 400) throw new RuntimeException(\"RevKeen request failed ($status)\");\necho $response;"},{"lang":"python","label":"Python","source":"import os\n\nimport requests\n\nresponse = requests.patch(\n    \"https://api.revkeen.com/v2/meters/11111111-1111-4111-8111-111111111111\",\n    headers={\"x-api-key\": os.environ[\"REVKEEN_API_KEY\"]},\n    json={\n        \"name\": \"example\",\n        \"slug\": \"example\",\n        \"description\": \"Example request from API docs\",\n        \"status\": \"active\",\n    },\n)\n\nresponse.raise_for_status()\nprint(response.json())"},{"lang":"javascript","label":"JavaScript","source":"const credential = process.env.REVKEEN_API_KEY;\nif (!credential) throw new Error(\"Set REVKEEN_API_KEY\");\nconst response = await fetch(\"https://api.revkeen.com/v2/meters/11111111-1111-4111-8111-111111111111\", {\n  method: \"PATCH\",\n  headers: {\n    \"x-api-key\": credential,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n    \"name\": \"example\",\n    \"slug\": \"example\",\n    \"description\": \"Example request from API docs\",\n    \"status\": \"active\"\n  }),\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"java","label":"Java","source":"import java.net.URI;\nimport java.net.http.HttpClient;\nimport java.net.http.HttpRequest;\nimport java.net.http.HttpResponse;\n\nHttpClient client = HttpClient.newHttpClient();\n\nHttpRequest request = HttpRequest.newBuilder()\n    .uri(URI.create(\"https://api.revkeen.com/v2/meters/11111111-1111-4111-8111-111111111111\"))\n    .header(\"x-api-key\", System.getenv(\"REVKEEN_API_KEY\"))\n    .header(\"Content-Type\", \"application/json\")\n    .method(\"PATCH\", HttpRequest.BodyPublishers.ofString(\"\"\"\n        {\n          \"name\": \"example\",\n          \"slug\": \"example\",\n          \"description\": \"Example request from API docs\",\n          \"status\": \"active\"\n        }\n        \"\"\"))\n    .build();\n\nHttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());\nSystem.out.println(response.body());"},{"lang":"ruby","label":"Ruby","source":"require \"net/http\"\nrequire \"json\"\nrequire \"uri\"\n\nuri = URI(\"https://api.revkeen.com/v2/meters/11111111-1111-4111-8111-111111111111\")\nhttp = Net::HTTP.new(uri.host, uri.port)\nhttp.use_ssl = true\n\nrequest = Net::HTTP::Patch.new(uri)\nrequest[\"x-api-key\"] = ENV[\"REVKEEN_API_KEY\"]\nrequest[\"Content-Type\"] = \"application/json\"\nrequest.body = {\n  \"name\" => \"example\",\n  \"slug\" => \"example\",\n  \"description\" => \"Example request from API docs\",\n  \"status\" => \"active\",\n}.to_json\n\nresponse = http.request(request)\nputs response.body"}]}},"/invoice-line-items/{id}/usage-events":{"get":{"operationId":"invoice_line_items_list_usage_events","tags":["Invoice Line Items"],"x-fern-sdk-group-name":["invoiceLineItems"],"x-fern-sdk-method-name":"listUsageEvents","summary":"List usage events for an invoice line","description":"Trace a usage invoice line back to the billing entries, meter matches, and events that produced it (REV-6228). FK-driven; does not re-rate from live prices.\n\n---\n\n**Common errors**\n\n- `400 invalid_request` — malformed payload or failed validation.\n- `401 authentication_error` — missing, invalid, expired, or revoked credential. Codes: `authentication_failed`, `invalid_api_key`, `expired_api_key`, `api_key_revoked`, `session_invalid`, `merchant_required`. Carries a `WWW-Authenticate: Bearer` challenge.\n- `404 resource_missing` — the referenced resource does not exist or is not visible to your key.\n\n**Pagination**\n\nOffset-based with `limit` (default 25, max 100) and `offset`. The response `pagination` block includes `total` and `hasMore`. See [the pagination guide](/docs/fundamentals/pagination) for SDK auto-paging helpers.","security":[{"apiKey":["invoices:read"]}],"parameters":[{"schema":{"type":"string","format":"uuid","description":"Invoice line item ID","example":"11111111-1111-4111-8111-111111111111"},"required":true,"description":"Invoice line item ID","name":"id","in":"path"},{"schema":{"type":"string","format":"uuid","description":"Cursor: return entries with id greater than this value"},"required":false,"description":"Cursor: return entries with id greater than this value","name":"starting_after","in":"query"},{"schema":{"type":"string","description":"Max billed entries to return (default 50, max 100)","example":"50"},"required":false,"description":"Max billed entries to return (default 50, max 100)","name":"limit","in":"query"},{"schema":{"type":"string","description":"When true, also return excluded-but-considered decisions for the line period","example":"false"},"required":false,"description":"When true, also return excluded-but-considered decisions for the line period","name":"include_excluded","in":"query"}],"responses":{"200":{"description":"Paginated usage provenance for the line","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InvoiceLineUsageEventsList"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InvoiceLineUsageEventsError"}}}},"401":{"description":"Unauthorized - Invalid or missing API key","headers":{"WWW-Authenticate":{"schema":{"type":"string","description":"RFC 6750 Bearer authentication challenge","example":"Bearer realm=\"revkeen-api\""},"required":true,"description":"RFC 6750 Bearer authentication challenge"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthenticationError"}}}},"404":{"description":"Line not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InvoiceLineUsageEventsError"}}}},"429":{"description":"Too Many Requests - Rate limit exceeded","headers":{"Retry-After":{"description":"Whole seconds until the caller may retry","schema":{"type":"string"},"example":"60"},"X-RateLimit-Limit":{"description":"Request quota for the current window","schema":{"type":"string"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window","schema":{"type":"string"}},"X-RateLimit-Reset":{"description":"UNIX timestamp in seconds when the window resets","schema":{"type":"string"}}}}},"x-cli-group":"invoice-line-items","x-cli-name":"list-usage-events","x-codeSamples":[{"lang":"curl","label":"cURL","source":"curl -X GET \"https://api.revkeen.com/v2/invoice-line-items/11111111-1111-4111-8111-111111111111/usage-events?starting_after=11111111-1111-4111-8111-111111111111&limit=50\" \\\n  -H \"x-api-key: $REVKEEN_API_KEY\""},{"lang":"typescript","label":"TypeScript (HTTP)","source":"const credential = process.env.REVKEEN_API_KEY;\nif (!credential) throw new Error(\"Set REVKEEN_API_KEY\");\nconst response = await fetch(\"https://api.revkeen.com/v2/invoice-line-items/11111111-1111-4111-8111-111111111111/usage-events?starting_after=11111111-1111-4111-8111-111111111111&limit=50\", {\n  method: \"GET\",\n  headers: {\n    \"x-api-key\": credential,\n  },\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"go","label":"Go (HTTP)","source":"package main\nimport (\"fmt\"; \"io\"; \"net/http\"; \"os\"; \"strings\"; \"time\")\nfunc main() {\n  req, err := http.NewRequest(\"GET\", \"https://api.revkeen.com/v2/invoice-line-items/11111111-1111-4111-8111-111111111111/usage-events?starting_after=11111111-1111-4111-8111-111111111111&limit=50\", strings.NewReader(\"\"))\n  if err != nil { panic(err) }\n  req.Header.Set(\"x-api-key\", os.Getenv(\"REVKEEN_API_KEY\"))\n  req.Header.Set(\"Content-Type\", \"application/json\")\n  client := &http.Client{Timeout: 10 * time.Second}\n  response, err := client.Do(req)\n  if err != nil { panic(err) }\n  defer response.Body.Close()\n  if response.StatusCode >= 400 { panic(fmt.Sprintf(\"RevKeen request failed (%d)\", response.StatusCode)) }\n  data, err := io.ReadAll(response.Body)\n  if err != nil { panic(err) }\n  fmt.Println(string(data))\n}"},{"lang":"php","label":"PHP (HTTP)","source":"<?php\n$request = curl_init(\"https://api.revkeen.com/v2/invoice-line-items/11111111-1111-4111-8111-111111111111/usage-events?starting_after=11111111-1111-4111-8111-111111111111&limit=50\");\ncurl_setopt_array($request, [\n  CURLOPT_CUSTOMREQUEST => \"GET\",\n  CURLOPT_RETURNTRANSFER => true,\n  CURLOPT_TIMEOUT => 10,\n  CURLOPT_HTTPHEADER => [\"x-api-key: \" . getenv(\"REVKEEN_API_KEY\"), \"Content-Type: application/json\"],\n]);\n$response = curl_exec($request);\nif ($response === false) throw new RuntimeException(curl_error($request));\n$status = curl_getinfo($request, CURLINFO_HTTP_CODE);\ncurl_close($request);\nif ($status >= 400) throw new RuntimeException(\"RevKeen request failed ($status)\");\necho $response;"},{"lang":"python","label":"Python","source":"import os\n\nimport requests\n\nresponse = requests.get(\n    \"https://api.revkeen.com/v2/invoice-line-items/11111111-1111-4111-8111-111111111111/usage-events\",\n    headers={\"x-api-key\": os.environ[\"REVKEEN_API_KEY\"]},\n    params={\n        \"starting_after\": \"11111111-1111-4111-8111-111111111111\",\n        \"limit\": \"50\",\n    },\n)\n\nresponse.raise_for_status()\nprint(response.json())"},{"lang":"javascript","label":"JavaScript","source":"const credential = process.env.REVKEEN_API_KEY;\nif (!credential) throw new Error(\"Set REVKEEN_API_KEY\");\nconst response = await fetch(\"https://api.revkeen.com/v2/invoice-line-items/11111111-1111-4111-8111-111111111111/usage-events?starting_after=11111111-1111-4111-8111-111111111111&limit=50\", {\n  method: \"GET\",\n  headers: {\n    \"x-api-key\": credential,\n  },\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"java","label":"Java","source":"import java.net.URI;\nimport java.net.http.HttpClient;\nimport java.net.http.HttpRequest;\nimport java.net.http.HttpResponse;\n\nHttpClient client = HttpClient.newHttpClient();\n\nHttpRequest request = HttpRequest.newBuilder()\n    .uri(URI.create(\"https://api.revkeen.com/v2/invoice-line-items/11111111-1111-4111-8111-111111111111/usage-events?starting_after=11111111-1111-4111-8111-111111111111&limit=50\"))\n    .header(\"x-api-key\", System.getenv(\"REVKEEN_API_KEY\"))\n    .GET()\n    .build();\n\nHttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());\nSystem.out.println(response.body());"},{"lang":"ruby","label":"Ruby","source":"require \"net/http\"\nrequire \"json\"\nrequire \"uri\"\n\nuri = URI(\"https://api.revkeen.com/v2/invoice-line-items/11111111-1111-4111-8111-111111111111/usage-events?starting_after=11111111-1111-4111-8111-111111111111&limit=50\")\nhttp = Net::HTTP.new(uri.host, uri.port)\nhttp.use_ssl = true\n\nrequest = Net::HTTP::Get.new(uri)\nrequest[\"x-api-key\"] = ENV[\"REVKEEN_API_KEY\"]\n\nresponse = http.request(request)\nputs response.body"}]}},"/integrations/accounting/invoice-payment-requests":{"post":{"operationId":"accounting_invoice_payment_requests_create","tags":["Accounting Integrations"],"summary":"Create an accounting-led invoice payment request","description":"Claims or reuses an ExternalAccountingInvoicePaymentRequest and mints a provider-neutral checkout session. Does not create a RevKeen invoice. Requires Idempotency-Key.\n\n---\n\n**Related endpoints**\n\n- `GET /integrations/accounting/invoice-payment-requests/{id}` — Get an accounting-led invoice payment request\n\n**Common errors**\n\n- `400 invalid_request` — malformed payload or failed validation.\n- `404 resource_missing` — the referenced resource does not exist or is not visible to your key.\n- `409 conflict` — Idempotency-Key collision with a different body, or a concurrent state-transition conflict.\n\n**Idempotency**\n\nPass an `Idempotency-Key` header (UUID v4 recommended) to make retries safe. Keys are valid for 24 hours; see [the idempotency guide](/docs/fundamentals/idempotency).","parameters":[{"name":"Idempotency-Key","in":"header","required":true,"description":"Required stable key for this logical payment request. Reuse the same key only with an identical request.","schema":{"type":"string","minLength":1}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateAccountingInvoicePaymentRequestInput"}}}},"responses":{"200":{"description":"Idempotent replay","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/AccountingInvoicePaymentRequest"},"requestId":{"type":"string"}},"required":["data"]}}}},"201":{"description":"Created","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/AccountingInvoicePaymentRequest"},"requestId":{"type":"string"}},"required":["data"]}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"401":{"description":"Unauthorized - Invalid or missing API key","headers":{"WWW-Authenticate":{"description":"RFC 6750 Bearer authentication challenge","schema":{"type":"string"},"example":"Bearer realm=\"revkeen-api\""}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"409":{"description":"Idempotency / conflict","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"429":{"description":"Too Many Requests - Rate limit exceeded","headers":{"Retry-After":{"description":"Whole seconds until the caller may retry","schema":{"type":"string"},"example":"60"},"X-RateLimit-Limit":{"description":"Request quota for the current window","schema":{"type":"string"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window","schema":{"type":"string"}},"X-RateLimit-Reset":{"description":"UNIX timestamp in seconds when the window resets","schema":{"type":"string"}}}}},"x-cli-group":"integrations","x-cli-name":"accounting-invoice-payment-requests-create","x-codeSamples":[{"lang":"curl","label":"cURL","source":"curl -X POST \"https://api.revkeen.com/v2/integrations/accounting/invoice-payment-requests\" \\\n  -H \"x-api-key: $REVKEEN_API_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"provider\": \"quickbooks_online\",\n    \"connection_id\": \"11111111-1111-4111-8111-111111111111\",\n    \"provider_account_id\": \"practicehub\",\n    \"external_invoice\": {\n      \"id\": \"rk_example_id\"\n    },\n    \"amount_due_minor\": 1000,\n    \"currency\": \"USD\"\n  }'"},{"lang":"typescript","label":"TypeScript (SDK)","source":"// npm install @revkeen/sdk@1.20260822.1541\nimport { RevKeenClient } from \"@revkeen/sdk\";\nconst apiKey = process.env.REVKEEN_API_KEY;\nif (!apiKey) throw new Error(\"Set REVKEEN_API_KEY\");\nconst client = new RevKeenClient({ apiKey, baseUrl: \"https://api.revkeen.com/v2\" });\nconst result = await client.accounting.invoicePaymentRequestsCreate({\n  \"provider\": \"quickbooks_online\",\n  \"connection_id\": \"11111111-1111-4111-8111-111111111111\",\n  \"provider_account_id\": \"practicehub\",\n  \"external_invoice\": {\n    \"id\": \"rk_example_id\"\n  },\n  \"amount_due_minor\": 1000,\n  \"currency\": \"USD\"\n});\nconsole.log(result);"},{"lang":"go","label":"Go (HTTP)","source":"package main\nimport (\"fmt\"; \"io\"; \"net/http\"; \"os\"; \"strings\"; \"time\")\nfunc main() {\n  req, err := http.NewRequest(\"POST\", \"https://api.revkeen.com/v2/integrations/accounting/invoice-payment-requests\", strings.NewReader(\"{\\\"provider\\\":\\\"quickbooks_online\\\",\\\"connection_id\\\":\\\"11111111-1111-4111-8111-111111111111\\\",\\\"provider_account_id\\\":\\\"practicehub\\\",\\\"external_invoice\\\":{\\\"id\\\":\\\"rk_example_id\\\"},\\\"amount_due_minor\\\":1000,\\\"currency\\\":\\\"USD\\\"}\"))\n  if err != nil { panic(err) }\n  req.Header.Set(\"x-api-key\", os.Getenv(\"REVKEEN_API_KEY\"))\n  req.Header.Set(\"Content-Type\", \"application/json\")\n  client := &http.Client{Timeout: 10 * time.Second}\n  response, err := client.Do(req)\n  if err != nil { panic(err) }\n  defer response.Body.Close()\n  if response.StatusCode >= 400 { panic(fmt.Sprintf(\"RevKeen request failed (%d)\", response.StatusCode)) }\n  data, err := io.ReadAll(response.Body)\n  if err != nil { panic(err) }\n  fmt.Println(string(data))\n}"},{"lang":"php","label":"PHP (HTTP)","source":"<?php\n$request = curl_init(\"https://api.revkeen.com/v2/integrations/accounting/invoice-payment-requests\");\ncurl_setopt_array($request, [\n  CURLOPT_CUSTOMREQUEST => \"POST\",\n  CURLOPT_RETURNTRANSFER => true,\n  CURLOPT_TIMEOUT => 10,\n  CURLOPT_HTTPHEADER => [\"x-api-key: \" . getenv(\"REVKEEN_API_KEY\"), \"Content-Type: application/json\"],\n  CURLOPT_POSTFIELDS => '{\\\"provider\\\":\\\"quickbooks_online\\\",\\\"connection_id\\\":\\\"11111111-1111-4111-8111-111111111111\\\",\\\"provider_account_id\\\":\\\"practicehub\\\",\\\"external_invoice\\\":{\\\"id\\\":\\\"rk_example_id\\\"},\\\"amount_due_minor\\\":1000,\\\"currency\\\":\\\"USD\\\"}',\n]);\n$response = curl_exec($request);\nif ($response === false) throw new RuntimeException(curl_error($request));\n$status = curl_getinfo($request, CURLINFO_HTTP_CODE);\ncurl_close($request);\nif ($status >= 400) throw new RuntimeException(\"RevKeen request failed ($status)\");\necho $response;"},{"lang":"python","label":"Python","source":"import os\n\nimport requests\n\nresponse = requests.post(\n    \"https://api.revkeen.com/v2/integrations/accounting/invoice-payment-requests\",\n    headers={\"x-api-key\": os.environ[\"REVKEEN_API_KEY\"]},\n    json={\n        \"provider\": \"quickbooks_online\",\n        \"connection_id\": \"11111111-1111-4111-8111-111111111111\",\n        \"provider_account_id\": \"practicehub\",\n        \"external_invoice\": {\n            \"id\": \"rk_example_id\",\n        },\n        \"amount_due_minor\": 1000,\n        \"currency\": \"USD\",\n    },\n)\n\nresponse.raise_for_status()\nprint(response.json())"},{"lang":"javascript","label":"JavaScript","source":"const credential = process.env.REVKEEN_API_KEY;\nif (!credential) throw new Error(\"Set REVKEEN_API_KEY\");\nconst response = await fetch(\"https://api.revkeen.com/v2/integrations/accounting/invoice-payment-requests\", {\n  method: \"POST\",\n  headers: {\n    \"x-api-key\": credential,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n    \"provider\": \"quickbooks_online\",\n    \"connection_id\": \"11111111-1111-4111-8111-111111111111\",\n    \"provider_account_id\": \"practicehub\",\n    \"external_invoice\": {\n      \"id\": \"rk_example_id\"\n    },\n    \"amount_due_minor\": 1000,\n    \"currency\": \"USD\"\n  }),\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"java","label":"Java","source":"import java.net.URI;\nimport java.net.http.HttpClient;\nimport java.net.http.HttpRequest;\nimport java.net.http.HttpResponse;\n\nHttpClient client = HttpClient.newHttpClient();\n\nHttpRequest request = HttpRequest.newBuilder()\n    .uri(URI.create(\"https://api.revkeen.com/v2/integrations/accounting/invoice-payment-requests\"))\n    .header(\"x-api-key\", System.getenv(\"REVKEEN_API_KEY\"))\n    .header(\"Content-Type\", \"application/json\")\n    .method(\"POST\", HttpRequest.BodyPublishers.ofString(\"\"\"\n        {\n          \"provider\": \"quickbooks_online\",\n          \"connection_id\": \"11111111-1111-4111-8111-111111111111\",\n          \"provider_account_id\": \"practicehub\",\n          \"external_invoice\": {\n            \"id\": \"rk_example_id\"\n          },\n          \"amount_due_minor\": 1000,\n          \"currency\": \"USD\"\n        }\n        \"\"\"))\n    .build();\n\nHttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());\nSystem.out.println(response.body());"},{"lang":"ruby","label":"Ruby","source":"require \"net/http\"\nrequire \"json\"\nrequire \"uri\"\n\nuri = URI(\"https://api.revkeen.com/v2/integrations/accounting/invoice-payment-requests\")\nhttp = Net::HTTP.new(uri.host, uri.port)\nhttp.use_ssl = true\n\nrequest = Net::HTTP::Post.new(uri)\nrequest[\"x-api-key\"] = ENV[\"REVKEEN_API_KEY\"]\nrequest[\"Content-Type\"] = \"application/json\"\nrequest.body = {\n  \"provider\" => \"quickbooks_online\",\n  \"connection_id\" => \"11111111-1111-4111-8111-111111111111\",\n  \"provider_account_id\" => \"practicehub\",\n  \"external_invoice\" => {\n    \"id\" => \"rk_example_id\",\n  },\n  \"amount_due_minor\" => 1000,\n  \"currency\" => \"USD\",\n}.to_json\n\nresponse = http.request(request)\nputs response.body"}],"security":[{"apiKey":["checkout:write","integrations:write"]},{"oauth2":["checkout:write","integrations:write"]}]}},"/integrations/accounting/invoice-payment-requests/{id}":{"get":{"operationId":"accounting_invoice_payment_requests_get","tags":["Accounting Integrations"],"summary":"Get an accounting-led invoice payment request","parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/AccountingInvoicePaymentRequest"},"requestId":{"type":"string"}},"required":["data"]}}}},"401":{"description":"Unauthorized - Invalid or missing API key","headers":{"WWW-Authenticate":{"description":"RFC 6750 Bearer authentication challenge","schema":{"type":"string"},"example":"Bearer realm=\"revkeen-api\""}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"429":{"description":"Too Many Requests - Rate limit exceeded","headers":{"Retry-After":{"description":"Whole seconds until the caller may retry","schema":{"type":"string"},"example":"60"},"X-RateLimit-Limit":{"description":"Request quota for the current window","schema":{"type":"string"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window","schema":{"type":"string"}},"X-RateLimit-Reset":{"description":"UNIX timestamp in seconds when the window resets","schema":{"type":"string"}}}}},"x-cli-group":"integrations","x-cli-name":"accounting-invoice-payment-requests-get","x-codeSamples":[{"lang":"curl","label":"cURL","source":"curl -X GET \"https://api.revkeen.com/v2/integrations/accounting/invoice-payment-requests/11111111-1111-4111-8111-111111111111\" \\\n  -H \"x-api-key: $REVKEEN_API_KEY\""},{"lang":"typescript","label":"TypeScript (SDK)","source":"// npm install @revkeen/sdk@1.20260822.1541\nimport { RevKeenClient } from \"@revkeen/sdk\";\nconst apiKey = process.env.REVKEEN_API_KEY;\nif (!apiKey) throw new Error(\"Set REVKEEN_API_KEY\");\nconst client = new RevKeenClient({ apiKey, baseUrl: \"https://api.revkeen.com/v2\" });\nconst result = await client.accounting.invoicePaymentRequestsGet(\"11111111-1111-4111-8111-111111111111\");\nconsole.log(result);"},{"lang":"go","label":"Go (HTTP)","source":"package main\nimport (\"fmt\"; \"io\"; \"net/http\"; \"os\"; \"strings\"; \"time\")\nfunc main() {\n  req, err := http.NewRequest(\"GET\", \"https://api.revkeen.com/v2/integrations/accounting/invoice-payment-requests/11111111-1111-4111-8111-111111111111\", strings.NewReader(\"\"))\n  if err != nil { panic(err) }\n  req.Header.Set(\"x-api-key\", os.Getenv(\"REVKEEN_API_KEY\"))\n  req.Header.Set(\"Content-Type\", \"application/json\")\n  client := &http.Client{Timeout: 10 * time.Second}\n  response, err := client.Do(req)\n  if err != nil { panic(err) }\n  defer response.Body.Close()\n  if response.StatusCode >= 400 { panic(fmt.Sprintf(\"RevKeen request failed (%d)\", response.StatusCode)) }\n  data, err := io.ReadAll(response.Body)\n  if err != nil { panic(err) }\n  fmt.Println(string(data))\n}"},{"lang":"php","label":"PHP (HTTP)","source":"<?php\n$request = curl_init(\"https://api.revkeen.com/v2/integrations/accounting/invoice-payment-requests/11111111-1111-4111-8111-111111111111\");\ncurl_setopt_array($request, [\n  CURLOPT_CUSTOMREQUEST => \"GET\",\n  CURLOPT_RETURNTRANSFER => true,\n  CURLOPT_TIMEOUT => 10,\n  CURLOPT_HTTPHEADER => [\"x-api-key: \" . getenv(\"REVKEEN_API_KEY\"), \"Content-Type: application/json\"],\n]);\n$response = curl_exec($request);\nif ($response === false) throw new RuntimeException(curl_error($request));\n$status = curl_getinfo($request, CURLINFO_HTTP_CODE);\ncurl_close($request);\nif ($status >= 400) throw new RuntimeException(\"RevKeen request failed ($status)\");\necho $response;"},{"lang":"python","label":"Python","source":"import os\n\nimport requests\n\nresponse = requests.get(\n    \"https://api.revkeen.com/v2/integrations/accounting/invoice-payment-requests/11111111-1111-4111-8111-111111111111\",\n    headers={\"x-api-key\": os.environ[\"REVKEEN_API_KEY\"]},\n)\n\nresponse.raise_for_status()\nprint(response.json())"},{"lang":"javascript","label":"JavaScript","source":"const credential = process.env.REVKEEN_API_KEY;\nif (!credential) throw new Error(\"Set REVKEEN_API_KEY\");\nconst response = await fetch(\"https://api.revkeen.com/v2/integrations/accounting/invoice-payment-requests/11111111-1111-4111-8111-111111111111\", {\n  method: \"GET\",\n  headers: {\n    \"x-api-key\": credential,\n  },\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"java","label":"Java","source":"import java.net.URI;\nimport java.net.http.HttpClient;\nimport java.net.http.HttpRequest;\nimport java.net.http.HttpResponse;\n\nHttpClient client = HttpClient.newHttpClient();\n\nHttpRequest request = HttpRequest.newBuilder()\n    .uri(URI.create(\"https://api.revkeen.com/v2/integrations/accounting/invoice-payment-requests/11111111-1111-4111-8111-111111111111\"))\n    .header(\"x-api-key\", System.getenv(\"REVKEEN_API_KEY\"))\n    .GET()\n    .build();\n\nHttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());\nSystem.out.println(response.body());"},{"lang":"ruby","label":"Ruby","source":"require \"net/http\"\nrequire \"json\"\nrequire \"uri\"\n\nuri = URI(\"https://api.revkeen.com/v2/integrations/accounting/invoice-payment-requests/11111111-1111-4111-8111-111111111111\")\nhttp = Net::HTTP.new(uri.host, uri.port)\nhttp.use_ssl = true\n\nrequest = Net::HTTP::Get.new(uri)\nrequest[\"x-api-key\"] = ENV[\"REVKEEN_API_KEY\"]\n\nresponse = http.request(request)\nputs response.body"}],"description":"---\n\n**Related endpoints**\n\n- `POST /integrations/accounting/invoice-payment-requests` — Create an accounting-led invoice payment request\n\n**Common errors**\n\n- `404 resource_missing` — the referenced resource does not exist or is not visible to your key.","security":[{"apiKey":["integrations:read"]},{"oauth2":["integrations:read"]}]}},"/webhook-endpoints":{"get":{"operationId":"webhook_endpoints_list","tags":["Webhook Endpoints"],"x-fern-sdk-group-name":["webhookEndpoints"],"summary":"List webhook endpoints","description":"List all webhook endpoints for the authenticated merchant. Supports filtering by status and pagination.\n\n---\n\n**Related endpoints**\n\n- `POST /webhook-endpoints` — Create webhook endpoint\n- `GET /webhook-endpoints/{id}` — Get webhook endpoint\n- `PATCH /webhook-endpoints/{id}` — Update webhook endpoint\n- `DELETE /webhook-endpoints/{id}` — Delete webhook endpoint\n- `POST /webhook-endpoints/{id}/rotate-secret` — Rotate signing secret\n- `POST /webhook-endpoints/{id}/redeliver` — Redeliver an event to this endpoint\n\n**Common errors**\n\n- `401 authentication_error` — missing, invalid, expired, or revoked credential. Codes: `authentication_failed`, `invalid_api_key`, `expired_api_key`, `api_key_revoked`, `session_invalid`, `merchant_required`. Carries a `WWW-Authenticate: Bearer` challenge.\n\n**Pagination**\n\nOffset-based with `limit` (default 25, max 100) and `offset`. The response `pagination` block includes `total` and `hasMore`. See [the pagination guide](/docs/fundamentals/pagination) for SDK auto-paging helpers.","parameters":[{"schema":{"type":"string","enum":["enabled","disabled"],"description":"Filter by endpoint status"},"required":false,"description":"Filter by endpoint status","name":"status","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":100,"default":20,"description":"Maximum number of results to return (1-100, default 20)"},"required":false,"description":"Maximum number of results to return (1-100, default 20)","name":"limit","in":"query"},{"schema":{"type":["integer","null"],"minimum":0,"default":0,"description":"Number of results to skip for pagination"},"required":false,"description":"Number of results to skip for pagination","name":"offset","in":"query"}],"responses":{"200":{"description":"List of webhook endpoints","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Unique identifier for the webhook endpoint","example":"1b2c3d4e-5f6a-4b7c-9d8e-0f1a2b3c4d98"},"url":{"type":"string","format":"uri","description":"The URL that receives webhook POST requests","example":"https://example.com/webhooks/revkeen"},"description":{"type":["string","null"],"description":"Optional human-readable description","example":"Production webhook endpoint"},"enabled_events":{"type":"array","items":{"type":"string"},"description":"Event types this endpoint subscribes to","example":["invoice.paid","subscription.created"]},"status":{"type":"string","enum":["enabled","disabled"],"description":"Whether the endpoint is currently receiving events","example":"enabled"},"circuit_breaker_state":{"type":"string","enum":["closed","open","half_open"],"description":"Circuit breaker status. Opens after repeated delivery failures.","example":"closed"},"total_deliveries":{"type":"number","description":"Total number of delivery attempts","example":12},"successful_deliveries":{"type":"number","description":"Number of successful deliveries","example":12},"failed_deliveries":{"type":"number","description":"Number of failed deliveries","example":0},"last_delivery_at":{"type":["string","null"],"format":"date-time","description":"Timestamp of the most recent delivery attempt","example":"2026-04-02T11:30:00.000Z"},"created_at":{"type":"string","format":"date-time","description":"When the endpoint was created","example":"2026-04-02T10:00:00.000Z"},"updated_at":{"type":"string","format":"date-time","description":"When the endpoint was last modified","example":"2026-04-02T10:05:00.000Z"}},"required":["id","url","description","enabled_events","status","circuit_breaker_state","total_deliveries","successful_deliveries","failed_deliveries","last_delivery_at","created_at","updated_at"]}},"meta":{"type":"object","properties":{"total":{"type":"number","description":"Total number of results","example":42},"limit":{"type":"number","description":"Page size","example":20},"offset":{"type":"number","description":"Number of results skipped","example":0},"has_more":{"type":"boolean","description":"Whether more results exist beyond this page","example":true}},"required":["total","limit","offset","has_more"]}},"required":["data","meta"]}}}},"401":{"description":"Authentication required","headers":{"WWW-Authenticate":{"description":"RFC 6750 Bearer authentication challenge","schema":{"type":"string"},"example":"Bearer realm=\"revkeen-api\""}}},"429":{"description":"Too Many Requests - Rate limit exceeded","headers":{"Retry-After":{"description":"Whole seconds until the caller may retry","schema":{"type":"string"},"example":"60"},"X-RateLimit-Limit":{"description":"Request quota for the current window","schema":{"type":"string"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window","schema":{"type":"string"}},"X-RateLimit-Reset":{"description":"UNIX timestamp in seconds when the window resets","schema":{"type":"string"}}}}},"x-cli-group":"webhook-endpoints","x-cli-name":"list","x-codeSamples":[{"lang":"curl","label":"cURL","source":"curl -X GET \"https://api.revkeen.com/v2/webhook-endpoints?status=enabled&limit=20\" \\\n  -H \"x-api-key: $REVKEEN_API_KEY\""},{"lang":"typescript","label":"TypeScript (SDK)","source":"// npm install @revkeen/sdk@1.20260822.1541\nimport { RevKeenClient } from \"@revkeen/sdk\";\nconst apiKey = process.env.REVKEEN_API_KEY;\nif (!apiKey) throw new Error(\"Set REVKEEN_API_KEY\");\nconst client = new RevKeenClient({ apiKey, baseUrl: \"https://api.revkeen.com/v2\" });\nconst result = await client.webhookEndpoints.list({\n  \"status\": \"enabled\",\n  \"limit\": 20\n});\nconsole.log(result);"},{"lang":"go","label":"Go (HTTP)","source":"package main\nimport (\"fmt\"; \"io\"; \"net/http\"; \"os\"; \"strings\"; \"time\")\nfunc main() {\n  req, err := http.NewRequest(\"GET\", \"https://api.revkeen.com/v2/webhook-endpoints?status=enabled&limit=20\", strings.NewReader(\"\"))\n  if err != nil { panic(err) }\n  req.Header.Set(\"x-api-key\", os.Getenv(\"REVKEEN_API_KEY\"))\n  req.Header.Set(\"Content-Type\", \"application/json\")\n  client := &http.Client{Timeout: 10 * time.Second}\n  response, err := client.Do(req)\n  if err != nil { panic(err) }\n  defer response.Body.Close()\n  if response.StatusCode >= 400 { panic(fmt.Sprintf(\"RevKeen request failed (%d)\", response.StatusCode)) }\n  data, err := io.ReadAll(response.Body)\n  if err != nil { panic(err) }\n  fmt.Println(string(data))\n}"},{"lang":"php","label":"PHP (HTTP)","source":"<?php\n$request = curl_init(\"https://api.revkeen.com/v2/webhook-endpoints?status=enabled&limit=20\");\ncurl_setopt_array($request, [\n  CURLOPT_CUSTOMREQUEST => \"GET\",\n  CURLOPT_RETURNTRANSFER => true,\n  CURLOPT_TIMEOUT => 10,\n  CURLOPT_HTTPHEADER => [\"x-api-key: \" . getenv(\"REVKEEN_API_KEY\"), \"Content-Type: application/json\"],\n]);\n$response = curl_exec($request);\nif ($response === false) throw new RuntimeException(curl_error($request));\n$status = curl_getinfo($request, CURLINFO_HTTP_CODE);\ncurl_close($request);\nif ($status >= 400) throw new RuntimeException(\"RevKeen request failed ($status)\");\necho $response;"},{"lang":"python","label":"Python","source":"import os\n\nimport requests\n\nresponse = requests.get(\n    \"https://api.revkeen.com/v2/webhook-endpoints\",\n    headers={\"x-api-key\": os.environ[\"REVKEEN_API_KEY\"]},\n    params={\n        \"status\": \"enabled\",\n        \"limit\": 20,\n    },\n)\n\nresponse.raise_for_status()\nprint(response.json())"},{"lang":"javascript","label":"JavaScript","source":"const credential = process.env.REVKEEN_API_KEY;\nif (!credential) throw new Error(\"Set REVKEEN_API_KEY\");\nconst response = await fetch(\"https://api.revkeen.com/v2/webhook-endpoints?status=enabled&limit=20\", {\n  method: \"GET\",\n  headers: {\n    \"x-api-key\": credential,\n  },\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"java","label":"Java","source":"import java.net.URI;\nimport java.net.http.HttpClient;\nimport java.net.http.HttpRequest;\nimport java.net.http.HttpResponse;\n\nHttpClient client = HttpClient.newHttpClient();\n\nHttpRequest request = HttpRequest.newBuilder()\n    .uri(URI.create(\"https://api.revkeen.com/v2/webhook-endpoints?status=enabled&limit=20\"))\n    .header(\"x-api-key\", System.getenv(\"REVKEEN_API_KEY\"))\n    .GET()\n    .build();\n\nHttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());\nSystem.out.println(response.body());"},{"lang":"ruby","label":"Ruby","source":"require \"net/http\"\nrequire \"json\"\nrequire \"uri\"\n\nuri = URI(\"https://api.revkeen.com/v2/webhook-endpoints?status=enabled&limit=20\")\nhttp = Net::HTTP.new(uri.host, uri.port)\nhttp.use_ssl = true\n\nrequest = Net::HTTP::Get.new(uri)\nrequest[\"x-api-key\"] = ENV[\"REVKEEN_API_KEY\"]\n\nresponse = http.request(request)\nputs response.body"}],"security":[{"apiKey":["webhooks:read"]},{"oauth2":["webhooks:read"]}]},"post":{"operationId":"webhook_endpoints_create","tags":["Webhook Endpoints"],"x-fern-sdk-group-name":["webhookEndpoints"],"summary":"Create webhook endpoint","description":"Create a webhook endpoint to receive event notifications at the given URL. Returns the endpoint with its signing secret — store the secret securely, it is only shown once.\n\n---\n\n**Related endpoints**\n\n- `GET /webhook-endpoints` — List webhook endpoints\n- `GET /webhook-endpoints/{id}` — Get webhook endpoint\n- `PATCH /webhook-endpoints/{id}` — Update webhook endpoint\n- `DELETE /webhook-endpoints/{id}` — Delete webhook endpoint\n- `POST /webhook-endpoints/{id}/rotate-secret` — Rotate signing secret\n- `POST /webhook-endpoints/{id}/redeliver` — Redeliver an event to this endpoint\n\n**Common errors**\n\n- `400 invalid_request` — malformed payload or failed validation.\n- `401 authentication_error` — missing, invalid, expired, or revoked credential. Codes: `authentication_failed`, `invalid_api_key`, `expired_api_key`, `api_key_revoked`, `session_invalid`, `merchant_required`. Carries a `WWW-Authenticate: Bearer` challenge.\n- `409 conflict` — Idempotency-Key collision with a different body, or a concurrent state-transition conflict.\n\n**Idempotency**\n\nPass an `Idempotency-Key` header (UUID v4 recommended) to make retries safe. Keys are valid for 24 hours; see [the idempotency guide](/docs/fundamentals/idempotency).","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"url":{"type":"string","format":"uri","description":"The URL to receive webhook POST requests. Must be HTTPS in production."},"enabled_events":{"type":"array","items":{"type":"string"},"minItems":1,"description":"Event types to subscribe to: `*` for every event, an event name (e.g. `invoice.paid`), or a prefix wildcard (e.g. `invoice.*`). Unknown names are rejected with 400 invalid_event_type; duplicates are removed. At least one event is required. `mandate.created` and `mandate.auddis_rejected` are accepted as deprecated no-ops: nothing sends them."},"description":{"type":"string","maxLength":500,"description":"Optional human-readable description (max 500 characters)"}},"required":["url","enabled_events"]}}}},"responses":{"201":{"description":"Created webhook endpoint","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/WebhookEndpoint"}},"required":["data"]}}}},"400":{"description":"Invalid request body, an enabled_events value that is not subscribable (invalid_event_type), or a URL that is not allowed (webhook_url_not_allowed)"},"401":{"description":"Authentication required","headers":{"WWW-Authenticate":{"description":"RFC 6750 Bearer authentication challenge","schema":{"type":"string"},"example":"Bearer realm=\"revkeen-api\""}}},"409":{"description":"Webhook endpoint already exists for this URL"},"429":{"description":"Too Many Requests - Rate limit exceeded","headers":{"Retry-After":{"description":"Whole seconds until the caller may retry","schema":{"type":"string"},"example":"60"},"X-RateLimit-Limit":{"description":"Request quota for the current window","schema":{"type":"string"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window","schema":{"type":"string"}},"X-RateLimit-Reset":{"description":"UNIX timestamp in seconds when the window resets","schema":{"type":"string"}}}}},"x-cli-group":"webhook-endpoints","x-cli-name":"create","x-codeSamples":[{"lang":"curl","label":"cURL","source":"curl -X POST \"https://api.revkeen.com/v2/webhook-endpoints\" \\\n  -H \"x-api-key: $REVKEEN_API_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"url\": \"https://example.com/success\",\n    \"enabled_events\": [\n      \"example\"\n    ]\n  }'"},{"lang":"typescript","label":"TypeScript (SDK)","source":"// npm install @revkeen/sdk@1.20260822.1541\nimport { RevKeenClient } from \"@revkeen/sdk\";\nconst apiKey = process.env.REVKEEN_API_KEY;\nif (!apiKey) throw new Error(\"Set REVKEEN_API_KEY\");\nconst client = new RevKeenClient({ apiKey, baseUrl: \"https://api.revkeen.com/v2\" });\nconst result = await client.webhookEndpoints.create({\n  \"url\": \"https://example.com/success\",\n  \"enabled_events\": [\n    \"example\"\n  ]\n});\nconsole.log(result);"},{"lang":"go","label":"Go (HTTP)","source":"package main\nimport (\"fmt\"; \"io\"; \"net/http\"; \"os\"; \"strings\"; \"time\")\nfunc main() {\n  req, err := http.NewRequest(\"POST\", \"https://api.revkeen.com/v2/webhook-endpoints\", strings.NewReader(\"{\\\"url\\\":\\\"https://example.com/success\\\",\\\"enabled_events\\\":[\\\"example\\\"]}\"))\n  if err != nil { panic(err) }\n  req.Header.Set(\"x-api-key\", os.Getenv(\"REVKEEN_API_KEY\"))\n  req.Header.Set(\"Content-Type\", \"application/json\")\n  client := &http.Client{Timeout: 10 * time.Second}\n  response, err := client.Do(req)\n  if err != nil { panic(err) }\n  defer response.Body.Close()\n  if response.StatusCode >= 400 { panic(fmt.Sprintf(\"RevKeen request failed (%d)\", response.StatusCode)) }\n  data, err := io.ReadAll(response.Body)\n  if err != nil { panic(err) }\n  fmt.Println(string(data))\n}"},{"lang":"php","label":"PHP (HTTP)","source":"<?php\n$request = curl_init(\"https://api.revkeen.com/v2/webhook-endpoints\");\ncurl_setopt_array($request, [\n  CURLOPT_CUSTOMREQUEST => \"POST\",\n  CURLOPT_RETURNTRANSFER => true,\n  CURLOPT_TIMEOUT => 10,\n  CURLOPT_HTTPHEADER => [\"x-api-key: \" . getenv(\"REVKEEN_API_KEY\"), \"Content-Type: application/json\"],\n  CURLOPT_POSTFIELDS => '{\\\"url\\\":\\\"https://example.com/success\\\",\\\"enabled_events\\\":[\\\"example\\\"]}',\n]);\n$response = curl_exec($request);\nif ($response === false) throw new RuntimeException(curl_error($request));\n$status = curl_getinfo($request, CURLINFO_HTTP_CODE);\ncurl_close($request);\nif ($status >= 400) throw new RuntimeException(\"RevKeen request failed ($status)\");\necho $response;"},{"lang":"python","label":"Python","source":"import os\n\nimport requests\n\nresponse = requests.post(\n    \"https://api.revkeen.com/v2/webhook-endpoints\",\n    headers={\"x-api-key\": os.environ[\"REVKEEN_API_KEY\"]},\n    json={\n        \"url\": \"https://example.com/success\",\n        \"enabled_events\": [\n            \"example\",\n        ],\n    },\n)\n\nresponse.raise_for_status()\nprint(response.json())"},{"lang":"javascript","label":"JavaScript","source":"const credential = process.env.REVKEEN_API_KEY;\nif (!credential) throw new Error(\"Set REVKEEN_API_KEY\");\nconst response = await fetch(\"https://api.revkeen.com/v2/webhook-endpoints\", {\n  method: \"POST\",\n  headers: {\n    \"x-api-key\": credential,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n    \"url\": \"https://example.com/success\",\n    \"enabled_events\": [\n      \"example\"\n    ]\n  }),\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"java","label":"Java","source":"import java.net.URI;\nimport java.net.http.HttpClient;\nimport java.net.http.HttpRequest;\nimport java.net.http.HttpResponse;\n\nHttpClient client = HttpClient.newHttpClient();\n\nHttpRequest request = HttpRequest.newBuilder()\n    .uri(URI.create(\"https://api.revkeen.com/v2/webhook-endpoints\"))\n    .header(\"x-api-key\", System.getenv(\"REVKEEN_API_KEY\"))\n    .header(\"Content-Type\", \"application/json\")\n    .method(\"POST\", HttpRequest.BodyPublishers.ofString(\"\"\"\n        {\n          \"url\": \"https://example.com/success\",\n          \"enabled_events\": [\n            \"example\"\n          ]\n        }\n        \"\"\"))\n    .build();\n\nHttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());\nSystem.out.println(response.body());"},{"lang":"ruby","label":"Ruby","source":"require \"net/http\"\nrequire \"json\"\nrequire \"uri\"\n\nuri = URI(\"https://api.revkeen.com/v2/webhook-endpoints\")\nhttp = Net::HTTP.new(uri.host, uri.port)\nhttp.use_ssl = true\n\nrequest = Net::HTTP::Post.new(uri)\nrequest[\"x-api-key\"] = ENV[\"REVKEEN_API_KEY\"]\nrequest[\"Content-Type\"] = \"application/json\"\nrequest.body = {\n  \"url\" => \"https://example.com/success\",\n  \"enabled_events\" => [\n    \"example\",\n  ],\n}.to_json\n\nresponse = http.request(request)\nputs response.body"}],"security":[{"apiKey":["webhooks:write"]},{"oauth2":["webhooks:write"]}]}},"/webhook-endpoints/{id}":{"get":{"operationId":"webhook_endpoints_get","tags":["Webhook Endpoints"],"x-fern-sdk-group-name":["webhookEndpoints"],"summary":"Get webhook endpoint","description":"Retrieve a single webhook endpoint by ID, including delivery statistics.\n\n---\n\n**Related endpoints**\n\n- `GET /webhook-endpoints` — List webhook endpoints\n- `POST /webhook-endpoints` — Create webhook endpoint\n- `PATCH /webhook-endpoints/{id}` — Update webhook endpoint\n- `DELETE /webhook-endpoints/{id}` — Delete webhook endpoint\n- `POST /webhook-endpoints/{id}/rotate-secret` — Rotate signing secret\n- `POST /webhook-endpoints/{id}/redeliver` — Redeliver an event to this endpoint\n\n**Common errors**\n\n- `401 authentication_error` — missing, invalid, expired, or revoked credential. Codes: `authentication_failed`, `invalid_api_key`, `expired_api_key`, `api_key_revoked`, `session_invalid`, `merchant_required`. Carries a `WWW-Authenticate: Bearer` challenge.\n- `404 resource_missing` — the referenced resource does not exist or is not visible to your key.","parameters":[{"schema":{"type":"string","format":"uuid","description":"Webhook endpoint ID"},"required":true,"description":"Webhook endpoint ID","name":"id","in":"path"}],"responses":{"200":{"description":"Webhook endpoint details","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Unique identifier for the webhook endpoint","example":"1b2c3d4e-5f6a-4b7c-9d8e-0f1a2b3c4d98"},"url":{"type":"string","format":"uri","description":"The URL that receives webhook POST requests","example":"https://example.com/webhooks/revkeen"},"description":{"type":["string","null"],"description":"Optional human-readable description","example":"Production webhook endpoint"},"enabled_events":{"type":"array","items":{"type":"string"},"description":"Event types this endpoint subscribes to","example":["invoice.paid","subscription.created"]},"status":{"type":"string","enum":["enabled","disabled"],"description":"Whether the endpoint is currently receiving events","example":"enabled"},"circuit_breaker_state":{"type":"string","enum":["closed","open","half_open"],"description":"Circuit breaker status. Opens after repeated delivery failures.","example":"closed"},"total_deliveries":{"type":"number","description":"Total number of delivery attempts","example":12},"successful_deliveries":{"type":"number","description":"Number of successful deliveries","example":12},"failed_deliveries":{"type":"number","description":"Number of failed deliveries","example":0},"last_delivery_at":{"type":["string","null"],"format":"date-time","description":"Timestamp of the most recent delivery attempt","example":"2026-04-02T11:30:00.000Z"},"created_at":{"type":"string","format":"date-time","description":"When the endpoint was created","example":"2026-04-02T10:00:00.000Z"},"updated_at":{"type":"string","format":"date-time","description":"When the endpoint was last modified","example":"2026-04-02T10:05:00.000Z"}},"required":["id","url","description","enabled_events","status","circuit_breaker_state","total_deliveries","successful_deliveries","failed_deliveries","last_delivery_at","created_at","updated_at"]}},"required":["data"]}}}},"401":{"description":"Authentication required","headers":{"WWW-Authenticate":{"description":"RFC 6750 Bearer authentication challenge","schema":{"type":"string"},"example":"Bearer realm=\"revkeen-api\""}}},"404":{"description":"Webhook endpoint not found"},"429":{"description":"Too Many Requests - Rate limit exceeded","headers":{"Retry-After":{"description":"Whole seconds until the caller may retry","schema":{"type":"string"},"example":"60"},"X-RateLimit-Limit":{"description":"Request quota for the current window","schema":{"type":"string"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window","schema":{"type":"string"}},"X-RateLimit-Reset":{"description":"UNIX timestamp in seconds when the window resets","schema":{"type":"string"}}}}},"x-cli-group":"webhook-endpoints","x-cli-name":"get","x-codeSamples":[{"lang":"curl","label":"cURL","source":"curl -X GET \"https://api.revkeen.com/v2/webhook-endpoints/11111111-1111-4111-8111-111111111111\" \\\n  -H \"x-api-key: $REVKEEN_API_KEY\""},{"lang":"typescript","label":"TypeScript (SDK)","source":"// npm install @revkeen/sdk@1.20260822.1541\nimport { RevKeenClient } from \"@revkeen/sdk\";\nconst apiKey = process.env.REVKEEN_API_KEY;\nif (!apiKey) throw new Error(\"Set REVKEEN_API_KEY\");\nconst client = new RevKeenClient({ apiKey, baseUrl: \"https://api.revkeen.com/v2\" });\nconst result = await client.webhookEndpoints.get(\"11111111-1111-4111-8111-111111111111\");\nconsole.log(result);"},{"lang":"go","label":"Go (HTTP)","source":"package main\nimport (\"fmt\"; \"io\"; \"net/http\"; \"os\"; \"strings\"; \"time\")\nfunc main() {\n  req, err := http.NewRequest(\"GET\", \"https://api.revkeen.com/v2/webhook-endpoints/11111111-1111-4111-8111-111111111111\", strings.NewReader(\"\"))\n  if err != nil { panic(err) }\n  req.Header.Set(\"x-api-key\", os.Getenv(\"REVKEEN_API_KEY\"))\n  req.Header.Set(\"Content-Type\", \"application/json\")\n  client := &http.Client{Timeout: 10 * time.Second}\n  response, err := client.Do(req)\n  if err != nil { panic(err) }\n  defer response.Body.Close()\n  if response.StatusCode >= 400 { panic(fmt.Sprintf(\"RevKeen request failed (%d)\", response.StatusCode)) }\n  data, err := io.ReadAll(response.Body)\n  if err != nil { panic(err) }\n  fmt.Println(string(data))\n}"},{"lang":"php","label":"PHP (HTTP)","source":"<?php\n$request = curl_init(\"https://api.revkeen.com/v2/webhook-endpoints/11111111-1111-4111-8111-111111111111\");\ncurl_setopt_array($request, [\n  CURLOPT_CUSTOMREQUEST => \"GET\",\n  CURLOPT_RETURNTRANSFER => true,\n  CURLOPT_TIMEOUT => 10,\n  CURLOPT_HTTPHEADER => [\"x-api-key: \" . getenv(\"REVKEEN_API_KEY\"), \"Content-Type: application/json\"],\n]);\n$response = curl_exec($request);\nif ($response === false) throw new RuntimeException(curl_error($request));\n$status = curl_getinfo($request, CURLINFO_HTTP_CODE);\ncurl_close($request);\nif ($status >= 400) throw new RuntimeException(\"RevKeen request failed ($status)\");\necho $response;"},{"lang":"python","label":"Python","source":"import os\n\nimport requests\n\nresponse = requests.get(\n    \"https://api.revkeen.com/v2/webhook-endpoints/11111111-1111-4111-8111-111111111111\",\n    headers={\"x-api-key\": os.environ[\"REVKEEN_API_KEY\"]},\n)\n\nresponse.raise_for_status()\nprint(response.json())"},{"lang":"javascript","label":"JavaScript","source":"const credential = process.env.REVKEEN_API_KEY;\nif (!credential) throw new Error(\"Set REVKEEN_API_KEY\");\nconst response = await fetch(\"https://api.revkeen.com/v2/webhook-endpoints/11111111-1111-4111-8111-111111111111\", {\n  method: \"GET\",\n  headers: {\n    \"x-api-key\": credential,\n  },\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"java","label":"Java","source":"import java.net.URI;\nimport java.net.http.HttpClient;\nimport java.net.http.HttpRequest;\nimport java.net.http.HttpResponse;\n\nHttpClient client = HttpClient.newHttpClient();\n\nHttpRequest request = HttpRequest.newBuilder()\n    .uri(URI.create(\"https://api.revkeen.com/v2/webhook-endpoints/11111111-1111-4111-8111-111111111111\"))\n    .header(\"x-api-key\", System.getenv(\"REVKEEN_API_KEY\"))\n    .GET()\n    .build();\n\nHttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());\nSystem.out.println(response.body());"},{"lang":"ruby","label":"Ruby","source":"require \"net/http\"\nrequire \"json\"\nrequire \"uri\"\n\nuri = URI(\"https://api.revkeen.com/v2/webhook-endpoints/11111111-1111-4111-8111-111111111111\")\nhttp = Net::HTTP.new(uri.host, uri.port)\nhttp.use_ssl = true\n\nrequest = Net::HTTP::Get.new(uri)\nrequest[\"x-api-key\"] = ENV[\"REVKEEN_API_KEY\"]\n\nresponse = http.request(request)\nputs response.body"}],"security":[{"apiKey":["webhooks:read"]},{"oauth2":["webhooks:read"]}]},"patch":{"operationId":"webhook_endpoints_update","tags":["Webhook Endpoints"],"x-fern-sdk-group-name":["webhookEndpoints"],"summary":"Update webhook endpoint","description":"Update a webhook endpoint's URL, subscribed events, description, or enabled status.\n\n---\n\n**Related endpoints**\n\n- `GET /webhook-endpoints` — List webhook endpoints\n- `POST /webhook-endpoints` — Create webhook endpoint\n- `GET /webhook-endpoints/{id}` — Get webhook endpoint\n- `DELETE /webhook-endpoints/{id}` — Delete webhook endpoint\n- `POST /webhook-endpoints/{id}/rotate-secret` — Rotate signing secret\n- `POST /webhook-endpoints/{id}/redeliver` — Redeliver an event to this endpoint\n\n**Common errors**\n\n- `400 invalid_request` — malformed payload or failed validation.\n- `401 authentication_error` — missing, invalid, expired, or revoked credential. Codes: `authentication_failed`, `invalid_api_key`, `expired_api_key`, `api_key_revoked`, `session_invalid`, `merchant_required`. Carries a `WWW-Authenticate: Bearer` challenge.\n- `404 resource_missing` — the referenced resource does not exist or is not visible to your key.\n\n**Idempotency**\n\nPass an `Idempotency-Key` header (UUID v4 recommended) to make retries safe. Keys are valid for 24 hours; see [the idempotency guide](/docs/fundamentals/idempotency).","parameters":[{"schema":{"type":"string","format":"uuid","description":"Webhook endpoint ID"},"required":true,"description":"Webhook endpoint ID","name":"id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"url":{"type":"string","format":"uri","description":"New URL for the endpoint"},"enabled_events":{"type":"array","items":{"type":"string"},"minItems":1,"description":"Replace the subscribed event types. Event types to subscribe to: `*` for every event, an event name (e.g. `invoice.paid`), or a prefix wildcard (e.g. `invoice.*`). Unknown names are rejected with 400 invalid_event_type; duplicates are removed. `mandate.created` and `mandate.auddis_rejected` are accepted as deprecated no-ops: nothing sends them."},"description":{"type":["string","null"],"maxLength":500,"description":"Update the description"},"enabled":{"type":"boolean","description":"Enable or disable the endpoint"}}}}}},"responses":{"200":{"description":"Updated webhook endpoint","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Unique identifier for the webhook endpoint","example":"1b2c3d4e-5f6a-4b7c-9d8e-0f1a2b3c4d98"},"url":{"type":"string","format":"uri","description":"The URL that receives webhook POST requests","example":"https://example.com/webhooks/revkeen"},"description":{"type":["string","null"],"description":"Optional human-readable description","example":"Production webhook endpoint"},"enabled_events":{"type":"array","items":{"type":"string"},"description":"Event types this endpoint subscribes to","example":["invoice.paid","subscription.created"]},"status":{"type":"string","enum":["enabled","disabled"],"description":"Whether the endpoint is currently receiving events","example":"enabled"},"circuit_breaker_state":{"type":"string","enum":["closed","open","half_open"],"description":"Circuit breaker status. Opens after repeated delivery failures.","example":"closed"},"total_deliveries":{"type":"number","description":"Total number of delivery attempts","example":12},"successful_deliveries":{"type":"number","description":"Number of successful deliveries","example":12},"failed_deliveries":{"type":"number","description":"Number of failed deliveries","example":0},"last_delivery_at":{"type":["string","null"],"format":"date-time","description":"Timestamp of the most recent delivery attempt","example":"2026-04-02T11:30:00.000Z"},"created_at":{"type":"string","format":"date-time","description":"When the endpoint was created","example":"2026-04-02T10:00:00.000Z"},"updated_at":{"type":"string","format":"date-time","description":"When the endpoint was last modified","example":"2026-04-02T10:05:00.000Z"}},"required":["id","url","description","enabled_events","status","circuit_breaker_state","total_deliveries","successful_deliveries","failed_deliveries","last_delivery_at","created_at","updated_at"]}},"required":["data"]}}}},"400":{"description":"Invalid request body, an enabled_events value that is not subscribable (invalid_event_type), or a URL that is not allowed (webhook_url_not_allowed)"},"401":{"description":"Authentication required","headers":{"WWW-Authenticate":{"description":"RFC 6750 Bearer authentication challenge","schema":{"type":"string"},"example":"Bearer realm=\"revkeen-api\""}}},"404":{"description":"Webhook endpoint not found"},"429":{"description":"Too Many Requests - Rate limit exceeded","headers":{"Retry-After":{"description":"Whole seconds until the caller may retry","schema":{"type":"string"},"example":"60"},"X-RateLimit-Limit":{"description":"Request quota for the current window","schema":{"type":"string"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window","schema":{"type":"string"}},"X-RateLimit-Reset":{"description":"UNIX timestamp in seconds when the window resets","schema":{"type":"string"}}}}},"x-cli-group":"webhook-endpoints","x-cli-name":"update","x-codeSamples":[{"lang":"curl","label":"cURL","source":"curl -X PATCH \"https://api.revkeen.com/v2/webhook-endpoints/11111111-1111-4111-8111-111111111111\" \\\n  -H \"x-api-key: $REVKEEN_API_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"url\": \"https://example.com/success\",\n    \"enabled_events\": [\n      \"example\"\n    ],\n    \"description\": \"Example request from API docs\",\n    \"enabled\": true\n  }'"},{"lang":"typescript","label":"TypeScript (SDK)","source":"// npm install @revkeen/sdk@1.20260822.1541\nimport { RevKeenClient } from \"@revkeen/sdk\";\nconst apiKey = process.env.REVKEEN_API_KEY;\nif (!apiKey) throw new Error(\"Set REVKEEN_API_KEY\");\nconst client = new RevKeenClient({ apiKey, baseUrl: \"https://api.revkeen.com/v2\" });\nconst result = await client.webhookEndpoints.update(\"11111111-1111-4111-8111-111111111111\", {\n  \"url\": \"https://example.com/success\",\n  \"enabled_events\": [\n    \"example\"\n  ],\n  \"description\": \"Example request from API docs\",\n  \"enabled\": true\n});\nconsole.log(result);"},{"lang":"go","label":"Go (HTTP)","source":"package main\nimport (\"fmt\"; \"io\"; \"net/http\"; \"os\"; \"strings\"; \"time\")\nfunc main() {\n  req, err := http.NewRequest(\"PATCH\", \"https://api.revkeen.com/v2/webhook-endpoints/11111111-1111-4111-8111-111111111111\", strings.NewReader(\"{\\\"url\\\":\\\"https://example.com/success\\\",\\\"enabled_events\\\":[\\\"example\\\"],\\\"description\\\":\\\"Example request from API docs\\\",\\\"enabled\\\":true}\"))\n  if err != nil { panic(err) }\n  req.Header.Set(\"x-api-key\", os.Getenv(\"REVKEEN_API_KEY\"))\n  req.Header.Set(\"Content-Type\", \"application/json\")\n  client := &http.Client{Timeout: 10 * time.Second}\n  response, err := client.Do(req)\n  if err != nil { panic(err) }\n  defer response.Body.Close()\n  if response.StatusCode >= 400 { panic(fmt.Sprintf(\"RevKeen request failed (%d)\", response.StatusCode)) }\n  data, err := io.ReadAll(response.Body)\n  if err != nil { panic(err) }\n  fmt.Println(string(data))\n}"},{"lang":"php","label":"PHP (HTTP)","source":"<?php\n$request = curl_init(\"https://api.revkeen.com/v2/webhook-endpoints/11111111-1111-4111-8111-111111111111\");\ncurl_setopt_array($request, [\n  CURLOPT_CUSTOMREQUEST => \"PATCH\",\n  CURLOPT_RETURNTRANSFER => true,\n  CURLOPT_TIMEOUT => 10,\n  CURLOPT_HTTPHEADER => [\"x-api-key: \" . getenv(\"REVKEEN_API_KEY\"), \"Content-Type: application/json\"],\n  CURLOPT_POSTFIELDS => '{\\\"url\\\":\\\"https://example.com/success\\\",\\\"enabled_events\\\":[\\\"example\\\"],\\\"description\\\":\\\"Example request from API docs\\\",\\\"enabled\\\":true}',\n]);\n$response = curl_exec($request);\nif ($response === false) throw new RuntimeException(curl_error($request));\n$status = curl_getinfo($request, CURLINFO_HTTP_CODE);\ncurl_close($request);\nif ($status >= 400) throw new RuntimeException(\"RevKeen request failed ($status)\");\necho $response;"},{"lang":"python","label":"Python","source":"import os\n\nimport requests\n\nresponse = requests.patch(\n    \"https://api.revkeen.com/v2/webhook-endpoints/11111111-1111-4111-8111-111111111111\",\n    headers={\"x-api-key\": os.environ[\"REVKEEN_API_KEY\"]},\n    json={\n        \"url\": \"https://example.com/success\",\n        \"enabled_events\": [\n            \"example\",\n        ],\n        \"description\": \"Example request from API docs\",\n        \"enabled\": True,\n    },\n)\n\nresponse.raise_for_status()\nprint(response.json())"},{"lang":"javascript","label":"JavaScript","source":"const credential = process.env.REVKEEN_API_KEY;\nif (!credential) throw new Error(\"Set REVKEEN_API_KEY\");\nconst response = await fetch(\"https://api.revkeen.com/v2/webhook-endpoints/11111111-1111-4111-8111-111111111111\", {\n  method: \"PATCH\",\n  headers: {\n    \"x-api-key\": credential,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n    \"url\": \"https://example.com/success\",\n    \"enabled_events\": [\n      \"example\"\n    ],\n    \"description\": \"Example request from API docs\",\n    \"enabled\": true\n  }),\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"java","label":"Java","source":"import java.net.URI;\nimport java.net.http.HttpClient;\nimport java.net.http.HttpRequest;\nimport java.net.http.HttpResponse;\n\nHttpClient client = HttpClient.newHttpClient();\n\nHttpRequest request = HttpRequest.newBuilder()\n    .uri(URI.create(\"https://api.revkeen.com/v2/webhook-endpoints/11111111-1111-4111-8111-111111111111\"))\n    .header(\"x-api-key\", System.getenv(\"REVKEEN_API_KEY\"))\n    .header(\"Content-Type\", \"application/json\")\n    .method(\"PATCH\", HttpRequest.BodyPublishers.ofString(\"\"\"\n        {\n          \"url\": \"https://example.com/success\",\n          \"enabled_events\": [\n            \"example\"\n          ],\n          \"description\": \"Example request from API docs\",\n          \"enabled\": true\n        }\n        \"\"\"))\n    .build();\n\nHttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());\nSystem.out.println(response.body());"},{"lang":"ruby","label":"Ruby","source":"require \"net/http\"\nrequire \"json\"\nrequire \"uri\"\n\nuri = URI(\"https://api.revkeen.com/v2/webhook-endpoints/11111111-1111-4111-8111-111111111111\")\nhttp = Net::HTTP.new(uri.host, uri.port)\nhttp.use_ssl = true\n\nrequest = Net::HTTP::Patch.new(uri)\nrequest[\"x-api-key\"] = ENV[\"REVKEEN_API_KEY\"]\nrequest[\"Content-Type\"] = \"application/json\"\nrequest.body = {\n  \"url\" => \"https://example.com/success\",\n  \"enabled_events\" => [\n    \"example\",\n  ],\n  \"description\" => \"Example request from API docs\",\n  \"enabled\" => true,\n}.to_json\n\nresponse = http.request(request)\nputs response.body"}],"security":[{"apiKey":["webhooks:write"]},{"oauth2":["webhooks:write"]}]},"delete":{"operationId":"webhook_endpoints_delete","tags":["Webhook Endpoints"],"x-fern-sdk-group-name":["webhookEndpoints"],"summary":"Delete webhook endpoint","description":"Permanently delete a webhook endpoint and all its event subscriptions.\n\n---\n\n**Related endpoints**\n\n- `GET /webhook-endpoints` — List webhook endpoints\n- `POST /webhook-endpoints` — Create webhook endpoint\n- `GET /webhook-endpoints/{id}` — Get webhook endpoint\n- `PATCH /webhook-endpoints/{id}` — Update webhook endpoint\n- `POST /webhook-endpoints/{id}/rotate-secret` — Rotate signing secret\n- `POST /webhook-endpoints/{id}/redeliver` — Redeliver an event to this endpoint\n\n**Common errors**\n\n- `401 authentication_error` — missing, invalid, expired, or revoked credential. Codes: `authentication_failed`, `invalid_api_key`, `expired_api_key`, `api_key_revoked`, `session_invalid`, `merchant_required`. Carries a `WWW-Authenticate: Bearer` challenge.\n- `404 resource_missing` — the referenced resource does not exist or is not visible to your key.\n\n**Idempotency**\n\nPass an `Idempotency-Key` header (UUID v4 recommended) to make retries safe. Keys are valid for 24 hours; see [the idempotency guide](/docs/fundamentals/idempotency).","parameters":[{"schema":{"type":"string","format":"uuid","description":"Webhook endpoint ID"},"required":true,"description":"Webhook endpoint ID","name":"id","in":"path"}],"responses":{"200":{"description":"Deleted webhook endpoint","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"deleted":{"type":"boolean","enum":[true]}},"required":["id","deleted"]}},"required":["data"]}}}},"401":{"description":"Authentication required","headers":{"WWW-Authenticate":{"description":"RFC 6750 Bearer authentication challenge","schema":{"type":"string"},"example":"Bearer realm=\"revkeen-api\""}}},"404":{"description":"Webhook endpoint not found"},"429":{"description":"Too Many Requests - Rate limit exceeded","headers":{"Retry-After":{"description":"Whole seconds until the caller may retry","schema":{"type":"string"},"example":"60"},"X-RateLimit-Limit":{"description":"Request quota for the current window","schema":{"type":"string"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window","schema":{"type":"string"}},"X-RateLimit-Reset":{"description":"UNIX timestamp in seconds when the window resets","schema":{"type":"string"}}}}},"x-cli-group":"webhook-endpoints","x-cli-name":"delete","x-codeSamples":[{"lang":"curl","label":"cURL","source":"curl -X DELETE \"https://api.revkeen.com/v2/webhook-endpoints/11111111-1111-4111-8111-111111111111\" \\\n  -H \"x-api-key: $REVKEEN_API_KEY\""},{"lang":"typescript","label":"TypeScript (SDK)","source":"// npm install @revkeen/sdk@1.20260822.1541\nimport { RevKeenClient } from \"@revkeen/sdk\";\nconst apiKey = process.env.REVKEEN_API_KEY;\nif (!apiKey) throw new Error(\"Set REVKEEN_API_KEY\");\nconst client = new RevKeenClient({ apiKey, baseUrl: \"https://api.revkeen.com/v2\" });\nconst result = await client.webhookEndpoints.delete(\"11111111-1111-4111-8111-111111111111\");\nconsole.log(result);"},{"lang":"go","label":"Go (HTTP)","source":"package main\nimport (\"fmt\"; \"io\"; \"net/http\"; \"os\"; \"strings\"; \"time\")\nfunc main() {\n  req, err := http.NewRequest(\"DELETE\", \"https://api.revkeen.com/v2/webhook-endpoints/11111111-1111-4111-8111-111111111111\", strings.NewReader(\"\"))\n  if err != nil { panic(err) }\n  req.Header.Set(\"x-api-key\", os.Getenv(\"REVKEEN_API_KEY\"))\n  req.Header.Set(\"Content-Type\", \"application/json\")\n  client := &http.Client{Timeout: 10 * time.Second}\n  response, err := client.Do(req)\n  if err != nil { panic(err) }\n  defer response.Body.Close()\n  if response.StatusCode >= 400 { panic(fmt.Sprintf(\"RevKeen request failed (%d)\", response.StatusCode)) }\n  data, err := io.ReadAll(response.Body)\n  if err != nil { panic(err) }\n  fmt.Println(string(data))\n}"},{"lang":"php","label":"PHP (HTTP)","source":"<?php\n$request = curl_init(\"https://api.revkeen.com/v2/webhook-endpoints/11111111-1111-4111-8111-111111111111\");\ncurl_setopt_array($request, [\n  CURLOPT_CUSTOMREQUEST => \"DELETE\",\n  CURLOPT_RETURNTRANSFER => true,\n  CURLOPT_TIMEOUT => 10,\n  CURLOPT_HTTPHEADER => [\"x-api-key: \" . getenv(\"REVKEEN_API_KEY\"), \"Content-Type: application/json\"],\n]);\n$response = curl_exec($request);\nif ($response === false) throw new RuntimeException(curl_error($request));\n$status = curl_getinfo($request, CURLINFO_HTTP_CODE);\ncurl_close($request);\nif ($status >= 400) throw new RuntimeException(\"RevKeen request failed ($status)\");\necho $response;"},{"lang":"python","label":"Python","source":"import os\n\nimport requests\n\nresponse = requests.delete(\n    \"https://api.revkeen.com/v2/webhook-endpoints/11111111-1111-4111-8111-111111111111\",\n    headers={\"x-api-key\": os.environ[\"REVKEEN_API_KEY\"]},\n)\n\nresponse.raise_for_status()\nprint(response.json())"},{"lang":"javascript","label":"JavaScript","source":"const credential = process.env.REVKEEN_API_KEY;\nif (!credential) throw new Error(\"Set REVKEEN_API_KEY\");\nconst response = await fetch(\"https://api.revkeen.com/v2/webhook-endpoints/11111111-1111-4111-8111-111111111111\", {\n  method: \"DELETE\",\n  headers: {\n    \"x-api-key\": credential,\n  },\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"java","label":"Java","source":"import java.net.URI;\nimport java.net.http.HttpClient;\nimport java.net.http.HttpRequest;\nimport java.net.http.HttpResponse;\n\nHttpClient client = HttpClient.newHttpClient();\n\nHttpRequest request = HttpRequest.newBuilder()\n    .uri(URI.create(\"https://api.revkeen.com/v2/webhook-endpoints/11111111-1111-4111-8111-111111111111\"))\n    .header(\"x-api-key\", System.getenv(\"REVKEEN_API_KEY\"))\n    .method(\"DELETE\", HttpRequest.BodyPublishers.noBody())\n    .build();\n\nHttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());\nSystem.out.println(response.body());"},{"lang":"ruby","label":"Ruby","source":"require \"net/http\"\nrequire \"json\"\nrequire \"uri\"\n\nuri = URI(\"https://api.revkeen.com/v2/webhook-endpoints/11111111-1111-4111-8111-111111111111\")\nhttp = Net::HTTP.new(uri.host, uri.port)\nhttp.use_ssl = true\n\nrequest = Net::HTTP::Delete.new(uri)\nrequest[\"x-api-key\"] = ENV[\"REVKEEN_API_KEY\"]\n\nresponse = http.request(request)\nputs response.body"}],"security":[{"apiKey":["webhooks:write"]},{"oauth2":["webhooks:write"]}]}},"/webhook-endpoints/{id}/rotate-secret":{"post":{"operationId":"webhook_endpoints_rotate_secret","tags":["Webhook Endpoints"],"x-fern-sdk-group-name":["webhookEndpoints"],"summary":"Rotate signing secret","description":"Generate a new signing secret for a webhook endpoint. The old secret is immediately invalidated. Store the new secret securely — it is only returned in this response.\n\n---\n\n**Related endpoints**\n\n- `GET /webhook-endpoints` — List webhook endpoints\n- `POST /webhook-endpoints` — Create webhook endpoint\n- `GET /webhook-endpoints/{id}` — Get webhook endpoint\n- `PATCH /webhook-endpoints/{id}` — Update webhook endpoint\n- `DELETE /webhook-endpoints/{id}` — Delete webhook endpoint\n- `POST /webhook-endpoints/{id}/redeliver` — Redeliver an event to this endpoint\n\n**Common errors**\n\n- `400 invalid_request` — malformed payload or failed validation.\n- `401 authentication_error` — missing, invalid, expired, or revoked credential. Codes: `authentication_failed`, `invalid_api_key`, `expired_api_key`, `api_key_revoked`, `session_invalid`, `merchant_required`. Carries a `WWW-Authenticate: Bearer` challenge.\n- `404 resource_missing` — the referenced resource does not exist or is not visible to your key.\n\n**Idempotency**\n\nPass an `Idempotency-Key` header (UUID v4 recommended) to make retries safe. Keys are valid for 24 hours; see [the idempotency guide](/docs/fundamentals/idempotency).","parameters":[{"schema":{"type":"string","format":"uuid","description":"Webhook endpoint ID"},"required":true,"description":"Webhook endpoint ID","name":"id","in":"path"}],"responses":{"200":{"description":"Rotated signing secret","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/WebhookEndpoint"}},"required":["data"]}}}},"400":{"description":"The endpoint's stored URL is not allowed (webhook_url_not_allowed); the secret is not rotated"},"401":{"description":"Authentication required","headers":{"WWW-Authenticate":{"description":"RFC 6750 Bearer authentication challenge","schema":{"type":"string"},"example":"Bearer realm=\"revkeen-api\""}}},"404":{"description":"Webhook endpoint not found"},"429":{"description":"Too Many Requests - Rate limit exceeded","headers":{"Retry-After":{"description":"Whole seconds until the caller may retry","schema":{"type":"string"},"example":"60"},"X-RateLimit-Limit":{"description":"Request quota for the current window","schema":{"type":"string"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window","schema":{"type":"string"}},"X-RateLimit-Reset":{"description":"UNIX timestamp in seconds when the window resets","schema":{"type":"string"}}}}},"x-cli-group":"webhook-endpoints","x-cli-name":"rotate-secret","x-codeSamples":[{"lang":"curl","label":"cURL","source":"curl -X POST \"https://api.revkeen.com/v2/webhook-endpoints/11111111-1111-4111-8111-111111111111/rotate-secret\" \\\n  -H \"x-api-key: $REVKEEN_API_KEY\""},{"lang":"typescript","label":"TypeScript (SDK)","source":"// npm install @revkeen/sdk@1.20260822.1541\nimport { RevKeenClient } from \"@revkeen/sdk\";\nconst apiKey = process.env.REVKEEN_API_KEY;\nif (!apiKey) throw new Error(\"Set REVKEEN_API_KEY\");\nconst client = new RevKeenClient({ apiKey, baseUrl: \"https://api.revkeen.com/v2\" });\nconst result = await client.webhookEndpoints.rotateSecret(\"11111111-1111-4111-8111-111111111111\");\nconsole.log(result);"},{"lang":"go","label":"Go (HTTP)","source":"package main\nimport (\"fmt\"; \"io\"; \"net/http\"; \"os\"; \"strings\"; \"time\")\nfunc main() {\n  req, err := http.NewRequest(\"POST\", \"https://api.revkeen.com/v2/webhook-endpoints/11111111-1111-4111-8111-111111111111/rotate-secret\", strings.NewReader(\"\"))\n  if err != nil { panic(err) }\n  req.Header.Set(\"x-api-key\", os.Getenv(\"REVKEEN_API_KEY\"))\n  req.Header.Set(\"Content-Type\", \"application/json\")\n  client := &http.Client{Timeout: 10 * time.Second}\n  response, err := client.Do(req)\n  if err != nil { panic(err) }\n  defer response.Body.Close()\n  if response.StatusCode >= 400 { panic(fmt.Sprintf(\"RevKeen request failed (%d)\", response.StatusCode)) }\n  data, err := io.ReadAll(response.Body)\n  if err != nil { panic(err) }\n  fmt.Println(string(data))\n}"},{"lang":"php","label":"PHP (HTTP)","source":"<?php\n$request = curl_init(\"https://api.revkeen.com/v2/webhook-endpoints/11111111-1111-4111-8111-111111111111/rotate-secret\");\ncurl_setopt_array($request, [\n  CURLOPT_CUSTOMREQUEST => \"POST\",\n  CURLOPT_RETURNTRANSFER => true,\n  CURLOPT_TIMEOUT => 10,\n  CURLOPT_HTTPHEADER => [\"x-api-key: \" . getenv(\"REVKEEN_API_KEY\"), \"Content-Type: application/json\"],\n]);\n$response = curl_exec($request);\nif ($response === false) throw new RuntimeException(curl_error($request));\n$status = curl_getinfo($request, CURLINFO_HTTP_CODE);\ncurl_close($request);\nif ($status >= 400) throw new RuntimeException(\"RevKeen request failed ($status)\");\necho $response;"},{"lang":"python","label":"Python","source":"import os\n\nimport requests\n\nresponse = requests.post(\n    \"https://api.revkeen.com/v2/webhook-endpoints/11111111-1111-4111-8111-111111111111/rotate-secret\",\n    headers={\"x-api-key\": os.environ[\"REVKEEN_API_KEY\"]},\n)\n\nresponse.raise_for_status()\nprint(response.json())"},{"lang":"javascript","label":"JavaScript","source":"const credential = process.env.REVKEEN_API_KEY;\nif (!credential) throw new Error(\"Set REVKEEN_API_KEY\");\nconst response = await fetch(\"https://api.revkeen.com/v2/webhook-endpoints/11111111-1111-4111-8111-111111111111/rotate-secret\", {\n  method: \"POST\",\n  headers: {\n    \"x-api-key\": credential,\n  },\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"java","label":"Java","source":"import java.net.URI;\nimport java.net.http.HttpClient;\nimport java.net.http.HttpRequest;\nimport java.net.http.HttpResponse;\n\nHttpClient client = HttpClient.newHttpClient();\n\nHttpRequest request = HttpRequest.newBuilder()\n    .uri(URI.create(\"https://api.revkeen.com/v2/webhook-endpoints/11111111-1111-4111-8111-111111111111/rotate-secret\"))\n    .header(\"x-api-key\", System.getenv(\"REVKEEN_API_KEY\"))\n    .method(\"POST\", HttpRequest.BodyPublishers.noBody())\n    .build();\n\nHttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());\nSystem.out.println(response.body());"},{"lang":"ruby","label":"Ruby","source":"require \"net/http\"\nrequire \"json\"\nrequire \"uri\"\n\nuri = URI(\"https://api.revkeen.com/v2/webhook-endpoints/11111111-1111-4111-8111-111111111111/rotate-secret\")\nhttp = Net::HTTP.new(uri.host, uri.port)\nhttp.use_ssl = true\n\nrequest = Net::HTTP::Post.new(uri)\nrequest[\"x-api-key\"] = ENV[\"REVKEEN_API_KEY\"]\n\nresponse = http.request(request)\nputs response.body"}],"security":[{"apiKey":["webhooks:write"]},{"oauth2":["webhooks:write"]}]}},"/webhook-endpoints/{id}/redeliver":{"post":{"operationId":"webhook_endpoints_redeliver","tags":["Webhook Endpoints"],"x-fern-sdk-group-name":["webhookEndpoints"],"summary":"Redeliver an event to this endpoint","description":"Send an event to this endpoint again, with its original payload, and to no other endpoint. A new webhook delivery is created and queued at once; its attempts and retries are separate from earlier deliveries. While a redelivery of the same event to this endpoint is still `pending`, calling this again returns that delivery (200) instead of creating another. The endpoint must be enabled and subscribed to the event's type. Send an `Idempotency-Key` to make a retried request safe.\n\n---\n\n**Related endpoints**\n\n- `GET /webhook-endpoints` — List webhook endpoints\n- `POST /webhook-endpoints` — Create webhook endpoint\n- `GET /webhook-endpoints/{id}` — Get webhook endpoint\n- `PATCH /webhook-endpoints/{id}` — Update webhook endpoint\n- `DELETE /webhook-endpoints/{id}` — Delete webhook endpoint\n- `POST /webhook-endpoints/{id}/rotate-secret` — Rotate signing secret\n\n**Common errors**\n\n- `401 authentication_error` — missing, invalid, expired, or revoked credential. Codes: `authentication_failed`, `invalid_api_key`, `expired_api_key`, `api_key_revoked`, `session_invalid`, `merchant_required`. Carries a `WWW-Authenticate: Bearer` challenge.\n- `404 resource_missing` — the referenced resource does not exist or is not visible to your key.\n- `409 conflict` — Idempotency-Key collision with a different body, or a concurrent state-transition conflict.\n\n**Idempotency**\n\nPass an `Idempotency-Key` header (UUID v4 recommended) to make retries safe. Keys are valid for 24 hours; see [the idempotency guide](/docs/fundamentals/idempotency).","parameters":[{"schema":{"type":"string","format":"uuid","description":"Webhook endpoint ID"},"required":true,"description":"Webhook endpoint ID","name":"id","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"event_id":{"type":"string","format":"uuid","description":"The event to send again: the `event_id` of one of its webhook deliveries (see `GET /webhook-deliveries`).","example":"1f8d9e0b-3a2e-4d9a-b0fe-1c2a3f4d5e6f"}},"required":["event_id"]}}}},"responses":{"200":{"description":"A redelivery of this event to this endpoint is already pending; it is returned unchanged.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEndpointRedeliverResponse"}}}},"201":{"description":"A new delivery was created and queued.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEndpointRedeliverResponse"}}}},"401":{"description":"Authentication required","headers":{"WWW-Authenticate":{"description":"RFC 6750 Bearer authentication challenge","schema":{"type":"string"},"example":"Bearer realm=\"revkeen-api\""}}},"404":{"description":"Webhook endpoint or event not found (not_found)"},"409":{"description":"The endpoint is disabled (endpoint_disabled), or not subscribed to the event's type (endpoint_not_subscribed). Nothing is queued."},"429":{"description":"Too Many Requests - Rate limit exceeded","headers":{"Retry-After":{"description":"Whole seconds until the caller may retry","schema":{"type":"string"},"example":"60"},"X-RateLimit-Limit":{"description":"Request quota for the current window","schema":{"type":"string"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window","schema":{"type":"string"}},"X-RateLimit-Reset":{"description":"UNIX timestamp in seconds when the window resets","schema":{"type":"string"}}}}},"x-cli-group":"webhook-endpoints","x-cli-name":"redeliver","x-codeSamples":[{"lang":"curl","label":"cURL","source":"curl -X POST \"https://api.revkeen.com/v2/webhook-endpoints/11111111-1111-4111-8111-111111111111/redeliver\" \\\n  -H \"x-api-key: $REVKEEN_API_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"event_id\": \"11111111-1111-4111-8111-111111111111\"\n  }'"},{"lang":"typescript","label":"TypeScript (HTTP)","source":"const credential = process.env.REVKEEN_API_KEY;\nif (!credential) throw new Error(\"Set REVKEEN_API_KEY\");\nconst response = await fetch(\"https://api.revkeen.com/v2/webhook-endpoints/11111111-1111-4111-8111-111111111111/redeliver\", {\n  method: \"POST\",\n  headers: {\n    \"x-api-key\": credential,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n    \"event_id\": \"11111111-1111-4111-8111-111111111111\"\n  }),\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"go","label":"Go (HTTP)","source":"package main\nimport (\"fmt\"; \"io\"; \"net/http\"; \"os\"; \"strings\"; \"time\")\nfunc main() {\n  req, err := http.NewRequest(\"POST\", \"https://api.revkeen.com/v2/webhook-endpoints/11111111-1111-4111-8111-111111111111/redeliver\", strings.NewReader(\"{\\\"event_id\\\":\\\"11111111-1111-4111-8111-111111111111\\\"}\"))\n  if err != nil { panic(err) }\n  req.Header.Set(\"x-api-key\", os.Getenv(\"REVKEEN_API_KEY\"))\n  req.Header.Set(\"Content-Type\", \"application/json\")\n  client := &http.Client{Timeout: 10 * time.Second}\n  response, err := client.Do(req)\n  if err != nil { panic(err) }\n  defer response.Body.Close()\n  if response.StatusCode >= 400 { panic(fmt.Sprintf(\"RevKeen request failed (%d)\", response.StatusCode)) }\n  data, err := io.ReadAll(response.Body)\n  if err != nil { panic(err) }\n  fmt.Println(string(data))\n}"},{"lang":"php","label":"PHP (HTTP)","source":"<?php\n$request = curl_init(\"https://api.revkeen.com/v2/webhook-endpoints/11111111-1111-4111-8111-111111111111/redeliver\");\ncurl_setopt_array($request, [\n  CURLOPT_CUSTOMREQUEST => \"POST\",\n  CURLOPT_RETURNTRANSFER => true,\n  CURLOPT_TIMEOUT => 10,\n  CURLOPT_HTTPHEADER => [\"x-api-key: \" . getenv(\"REVKEEN_API_KEY\"), \"Content-Type: application/json\"],\n  CURLOPT_POSTFIELDS => '{\\\"event_id\\\":\\\"11111111-1111-4111-8111-111111111111\\\"}',\n]);\n$response = curl_exec($request);\nif ($response === false) throw new RuntimeException(curl_error($request));\n$status = curl_getinfo($request, CURLINFO_HTTP_CODE);\ncurl_close($request);\nif ($status >= 400) throw new RuntimeException(\"RevKeen request failed ($status)\");\necho $response;"},{"lang":"python","label":"Python","source":"import os\n\nimport requests\n\nresponse = requests.post(\n    \"https://api.revkeen.com/v2/webhook-endpoints/11111111-1111-4111-8111-111111111111/redeliver\",\n    headers={\"x-api-key\": os.environ[\"REVKEEN_API_KEY\"]},\n    json={\n        \"event_id\": \"11111111-1111-4111-8111-111111111111\",\n    },\n)\n\nresponse.raise_for_status()\nprint(response.json())"},{"lang":"javascript","label":"JavaScript","source":"const credential = process.env.REVKEEN_API_KEY;\nif (!credential) throw new Error(\"Set REVKEEN_API_KEY\");\nconst response = await fetch(\"https://api.revkeen.com/v2/webhook-endpoints/11111111-1111-4111-8111-111111111111/redeliver\", {\n  method: \"POST\",\n  headers: {\n    \"x-api-key\": credential,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n    \"event_id\": \"11111111-1111-4111-8111-111111111111\"\n  }),\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"java","label":"Java","source":"import java.net.URI;\nimport java.net.http.HttpClient;\nimport java.net.http.HttpRequest;\nimport java.net.http.HttpResponse;\n\nHttpClient client = HttpClient.newHttpClient();\n\nHttpRequest request = HttpRequest.newBuilder()\n    .uri(URI.create(\"https://api.revkeen.com/v2/webhook-endpoints/11111111-1111-4111-8111-111111111111/redeliver\"))\n    .header(\"x-api-key\", System.getenv(\"REVKEEN_API_KEY\"))\n    .header(\"Content-Type\", \"application/json\")\n    .method(\"POST\", HttpRequest.BodyPublishers.ofString(\"\"\"\n        {\n          \"event_id\": \"11111111-1111-4111-8111-111111111111\"\n        }\n        \"\"\"))\n    .build();\n\nHttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());\nSystem.out.println(response.body());"},{"lang":"ruby","label":"Ruby","source":"require \"net/http\"\nrequire \"json\"\nrequire \"uri\"\n\nuri = URI(\"https://api.revkeen.com/v2/webhook-endpoints/11111111-1111-4111-8111-111111111111/redeliver\")\nhttp = Net::HTTP.new(uri.host, uri.port)\nhttp.use_ssl = true\n\nrequest = Net::HTTP::Post.new(uri)\nrequest[\"x-api-key\"] = ENV[\"REVKEEN_API_KEY\"]\nrequest[\"Content-Type\"] = \"application/json\"\nrequest.body = {\n  \"event_id\" => \"11111111-1111-4111-8111-111111111111\",\n}.to_json\n\nresponse = http.request(request)\nputs response.body"}],"security":[{"apiKey":["webhooks:write"]},{"oauth2":["webhooks:write"]}]}},"/webhook-deliveries":{"get":{"operationId":"webhook_deliveries_list","tags":["Webhook Deliveries"],"x-fern-sdk-group-name":["webhookDeliveries"],"summary":"List webhook deliveries","description":"List individual webhook delivery attempts across all endpoints for the authenticated merchant. Use filters to scope to a specific endpoint, a specific event, or a specific delivery status. Results are returned in reverse chronological order.\n\n---\n\n**Related endpoints**\n\n- `GET /webhook-deliveries/{id}` — Retrieve a webhook delivery\n- `POST /webhook-deliveries/{id}/retry` — Retry a webhook delivery\n\n**Common errors**\n\n- `401 authentication_error` — missing, invalid, expired, or revoked credential. Codes: `authentication_failed`, `invalid_api_key`, `expired_api_key`, `api_key_revoked`, `session_invalid`, `merchant_required`. Carries a `WWW-Authenticate: Bearer` challenge.\n\n**Pagination**\n\nOffset-based with `limit` (default 25, max 100) and `offset`. The response `pagination` block includes `total` and `hasMore`. See [the pagination guide](/docs/fundamentals/pagination) for SDK auto-paging helpers.","parameters":[{"schema":{"type":"string","format":"uuid","description":"Filter by webhook endpoint ID"},"required":false,"description":"Filter by webhook endpoint ID","name":"endpoint_id","in":"query"},{"schema":{"type":"string","format":"uuid","description":"Filter by source event ID"},"required":false,"description":"Filter by source event ID","name":"event_id","in":"query"},{"schema":{"type":"string","enum":["pending","succeeded","failed","dead_lettered"],"description":"Filter by delivery status"},"required":false,"description":"Filter by delivery status","name":"status","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":100,"default":20,"description":"Maximum number of deliveries to return (1-100, default 20)."},"required":false,"description":"Maximum number of deliveries to return (1-100, default 20).","name":"limit","in":"query"},{"schema":{"type":"string","format":"uuid","description":"Cursor — return deliveries created before the row with this ID."},"required":false,"description":"Cursor — return deliveries created before the row with this ID.","name":"starting_after","in":"query"},{"schema":{"type":"string","format":"uuid","description":"Cursor — return deliveries created after the row with this ID."},"required":false,"description":"Cursor — return deliveries created after the row with this ID.","name":"ending_before","in":"query"}],"responses":{"200":{"description":"Paginated list of webhook deliveries.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookDeliveryListResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookDeliveryErrorResponse"}}},"headers":{"WWW-Authenticate":{"description":"RFC 6750 Bearer authentication challenge","schema":{"type":"string"},"example":"Bearer realm=\"revkeen-api\""}}},"429":{"description":"Too Many Requests - Rate limit exceeded","headers":{"Retry-After":{"description":"Whole seconds until the caller may retry","schema":{"type":"string"},"example":"60"},"X-RateLimit-Limit":{"description":"Request quota for the current window","schema":{"type":"string"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window","schema":{"type":"string"}},"X-RateLimit-Reset":{"description":"UNIX timestamp in seconds when the window resets","schema":{"type":"string"}}}}},"x-cli-group":"webhook-deliveries","x-cli-name":"list","x-codeSamples":[{"lang":"curl","label":"cURL","source":"curl -X GET \"https://api.revkeen.com/v2/webhook-deliveries?endpoint_id=11111111-1111-4111-8111-111111111111&event_id=11111111-1111-4111-8111-111111111111\" \\\n  -H \"x-api-key: $REVKEEN_API_KEY\""},{"lang":"typescript","label":"TypeScript (SDK)","source":"// npm install @revkeen/sdk@1.20260822.1541\nimport { RevKeenClient } from \"@revkeen/sdk\";\nconst apiKey = process.env.REVKEEN_API_KEY;\nif (!apiKey) throw new Error(\"Set REVKEEN_API_KEY\");\nconst client = new RevKeenClient({ apiKey, baseUrl: \"https://api.revkeen.com/v2\" });\nconst result = await client.webhook.deliveriesList({\n  \"endpoint_id\": \"11111111-1111-4111-8111-111111111111\",\n  \"event_id\": \"11111111-1111-4111-8111-111111111111\"\n});\nconsole.log(result);"},{"lang":"go","label":"Go (HTTP)","source":"package main\nimport (\"fmt\"; \"io\"; \"net/http\"; \"os\"; \"strings\"; \"time\")\nfunc main() {\n  req, err := http.NewRequest(\"GET\", \"https://api.revkeen.com/v2/webhook-deliveries?endpoint_id=11111111-1111-4111-8111-111111111111&event_id=11111111-1111-4111-8111-111111111111\", strings.NewReader(\"\"))\n  if err != nil { panic(err) }\n  req.Header.Set(\"x-api-key\", os.Getenv(\"REVKEEN_API_KEY\"))\n  req.Header.Set(\"Content-Type\", \"application/json\")\n  client := &http.Client{Timeout: 10 * time.Second}\n  response, err := client.Do(req)\n  if err != nil { panic(err) }\n  defer response.Body.Close()\n  if response.StatusCode >= 400 { panic(fmt.Sprintf(\"RevKeen request failed (%d)\", response.StatusCode)) }\n  data, err := io.ReadAll(response.Body)\n  if err != nil { panic(err) }\n  fmt.Println(string(data))\n}"},{"lang":"php","label":"PHP (HTTP)","source":"<?php\n$request = curl_init(\"https://api.revkeen.com/v2/webhook-deliveries?endpoint_id=11111111-1111-4111-8111-111111111111&event_id=11111111-1111-4111-8111-111111111111\");\ncurl_setopt_array($request, [\n  CURLOPT_CUSTOMREQUEST => \"GET\",\n  CURLOPT_RETURNTRANSFER => true,\n  CURLOPT_TIMEOUT => 10,\n  CURLOPT_HTTPHEADER => [\"x-api-key: \" . getenv(\"REVKEEN_API_KEY\"), \"Content-Type: application/json\"],\n]);\n$response = curl_exec($request);\nif ($response === false) throw new RuntimeException(curl_error($request));\n$status = curl_getinfo($request, CURLINFO_HTTP_CODE);\ncurl_close($request);\nif ($status >= 400) throw new RuntimeException(\"RevKeen request failed ($status)\");\necho $response;"},{"lang":"python","label":"Python","source":"import os\n\nimport requests\n\nresponse = requests.get(\n    \"https://api.revkeen.com/v2/webhook-deliveries\",\n    headers={\"x-api-key\": os.environ[\"REVKEEN_API_KEY\"]},\n    params={\n        \"endpoint_id\": \"11111111-1111-4111-8111-111111111111\",\n        \"event_id\": \"11111111-1111-4111-8111-111111111111\",\n    },\n)\n\nresponse.raise_for_status()\nprint(response.json())"},{"lang":"javascript","label":"JavaScript","source":"const credential = process.env.REVKEEN_API_KEY;\nif (!credential) throw new Error(\"Set REVKEEN_API_KEY\");\nconst response = await fetch(\"https://api.revkeen.com/v2/webhook-deliveries?endpoint_id=11111111-1111-4111-8111-111111111111&event_id=11111111-1111-4111-8111-111111111111\", {\n  method: \"GET\",\n  headers: {\n    \"x-api-key\": credential,\n  },\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"java","label":"Java","source":"import java.net.URI;\nimport java.net.http.HttpClient;\nimport java.net.http.HttpRequest;\nimport java.net.http.HttpResponse;\n\nHttpClient client = HttpClient.newHttpClient();\n\nHttpRequest request = HttpRequest.newBuilder()\n    .uri(URI.create(\"https://api.revkeen.com/v2/webhook-deliveries?endpoint_id=11111111-1111-4111-8111-111111111111&event_id=11111111-1111-4111-8111-111111111111\"))\n    .header(\"x-api-key\", System.getenv(\"REVKEEN_API_KEY\"))\n    .GET()\n    .build();\n\nHttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());\nSystem.out.println(response.body());"},{"lang":"ruby","label":"Ruby","source":"require \"net/http\"\nrequire \"json\"\nrequire \"uri\"\n\nuri = URI(\"https://api.revkeen.com/v2/webhook-deliveries?endpoint_id=11111111-1111-4111-8111-111111111111&event_id=11111111-1111-4111-8111-111111111111\")\nhttp = Net::HTTP.new(uri.host, uri.port)\nhttp.use_ssl = true\n\nrequest = Net::HTTP::Get.new(uri)\nrequest[\"x-api-key\"] = ENV[\"REVKEEN_API_KEY\"]\n\nresponse = http.request(request)\nputs response.body"}],"security":[{"apiKey":["webhooks:read"]},{"oauth2":["webhooks:read"]}]}},"/webhook-deliveries/{id}":{"get":{"operationId":"webhook_deliveries_get","tags":["Webhook Deliveries"],"x-fern-sdk-group-name":["webhookDeliveries"],"summary":"Retrieve a webhook delivery","description":"Retrieve the details of a single webhook delivery including its last attempt outcome.\n\n---\n\n**Related endpoints**\n\n- `GET /webhook-deliveries` — List webhook deliveries\n- `POST /webhook-deliveries/{id}/retry` — Retry a webhook delivery\n\n**Common errors**\n\n- `404 resource_missing` — the referenced resource does not exist or is not visible to your key.","parameters":[{"schema":{"type":"string","format":"uuid","description":"Webhook delivery ID"},"required":true,"description":"Webhook delivery ID","name":"id","in":"path"}],"responses":{"200":{"description":"Webhook delivery details","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookDeliveryResponse"}}}},"401":{"description":"Unauthorized - Invalid or missing API key","headers":{"WWW-Authenticate":{"description":"RFC 6750 Bearer authentication challenge","schema":{"type":"string"},"example":"Bearer realm=\"revkeen-api\""}}},"404":{"description":"Webhook delivery not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookDeliveryErrorResponse"}}}},"429":{"description":"Too Many Requests - Rate limit exceeded","headers":{"Retry-After":{"description":"Whole seconds until the caller may retry","schema":{"type":"string"},"example":"60"},"X-RateLimit-Limit":{"description":"Request quota for the current window","schema":{"type":"string"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window","schema":{"type":"string"}},"X-RateLimit-Reset":{"description":"UNIX timestamp in seconds when the window resets","schema":{"type":"string"}}}}},"x-cli-group":"webhook-deliveries","x-cli-name":"get","x-codeSamples":[{"lang":"curl","label":"cURL","source":"curl -X GET \"https://api.revkeen.com/v2/webhook-deliveries/11111111-1111-4111-8111-111111111111\" \\\n  -H \"x-api-key: $REVKEEN_API_KEY\""},{"lang":"typescript","label":"TypeScript (SDK)","source":"// npm install @revkeen/sdk@1.20260822.1541\nimport { RevKeenClient } from \"@revkeen/sdk\";\nconst apiKey = process.env.REVKEEN_API_KEY;\nif (!apiKey) throw new Error(\"Set REVKEEN_API_KEY\");\nconst client = new RevKeenClient({ apiKey, baseUrl: \"https://api.revkeen.com/v2\" });\nconst result = await client.webhook.deliveriesGet(\"11111111-1111-4111-8111-111111111111\");\nconsole.log(result);"},{"lang":"go","label":"Go (HTTP)","source":"package main\nimport (\"fmt\"; \"io\"; \"net/http\"; \"os\"; \"strings\"; \"time\")\nfunc main() {\n  req, err := http.NewRequest(\"GET\", \"https://api.revkeen.com/v2/webhook-deliveries/11111111-1111-4111-8111-111111111111\", strings.NewReader(\"\"))\n  if err != nil { panic(err) }\n  req.Header.Set(\"x-api-key\", os.Getenv(\"REVKEEN_API_KEY\"))\n  req.Header.Set(\"Content-Type\", \"application/json\")\n  client := &http.Client{Timeout: 10 * time.Second}\n  response, err := client.Do(req)\n  if err != nil { panic(err) }\n  defer response.Body.Close()\n  if response.StatusCode >= 400 { panic(fmt.Sprintf(\"RevKeen request failed (%d)\", response.StatusCode)) }\n  data, err := io.ReadAll(response.Body)\n  if err != nil { panic(err) }\n  fmt.Println(string(data))\n}"},{"lang":"php","label":"PHP (HTTP)","source":"<?php\n$request = curl_init(\"https://api.revkeen.com/v2/webhook-deliveries/11111111-1111-4111-8111-111111111111\");\ncurl_setopt_array($request, [\n  CURLOPT_CUSTOMREQUEST => \"GET\",\n  CURLOPT_RETURNTRANSFER => true,\n  CURLOPT_TIMEOUT => 10,\n  CURLOPT_HTTPHEADER => [\"x-api-key: \" . getenv(\"REVKEEN_API_KEY\"), \"Content-Type: application/json\"],\n]);\n$response = curl_exec($request);\nif ($response === false) throw new RuntimeException(curl_error($request));\n$status = curl_getinfo($request, CURLINFO_HTTP_CODE);\ncurl_close($request);\nif ($status >= 400) throw new RuntimeException(\"RevKeen request failed ($status)\");\necho $response;"},{"lang":"python","label":"Python","source":"import os\n\nimport requests\n\nresponse = requests.get(\n    \"https://api.revkeen.com/v2/webhook-deliveries/11111111-1111-4111-8111-111111111111\",\n    headers={\"x-api-key\": os.environ[\"REVKEEN_API_KEY\"]},\n)\n\nresponse.raise_for_status()\nprint(response.json())"},{"lang":"javascript","label":"JavaScript","source":"const credential = process.env.REVKEEN_API_KEY;\nif (!credential) throw new Error(\"Set REVKEEN_API_KEY\");\nconst response = await fetch(\"https://api.revkeen.com/v2/webhook-deliveries/11111111-1111-4111-8111-111111111111\", {\n  method: \"GET\",\n  headers: {\n    \"x-api-key\": credential,\n  },\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"java","label":"Java","source":"import java.net.URI;\nimport java.net.http.HttpClient;\nimport java.net.http.HttpRequest;\nimport java.net.http.HttpResponse;\n\nHttpClient client = HttpClient.newHttpClient();\n\nHttpRequest request = HttpRequest.newBuilder()\n    .uri(URI.create(\"https://api.revkeen.com/v2/webhook-deliveries/11111111-1111-4111-8111-111111111111\"))\n    .header(\"x-api-key\", System.getenv(\"REVKEEN_API_KEY\"))\n    .GET()\n    .build();\n\nHttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());\nSystem.out.println(response.body());"},{"lang":"ruby","label":"Ruby","source":"require \"net/http\"\nrequire \"json\"\nrequire \"uri\"\n\nuri = URI(\"https://api.revkeen.com/v2/webhook-deliveries/11111111-1111-4111-8111-111111111111\")\nhttp = Net::HTTP.new(uri.host, uri.port)\nhttp.use_ssl = true\n\nrequest = Net::HTTP::Get.new(uri)\nrequest[\"x-api-key\"] = ENV[\"REVKEEN_API_KEY\"]\n\nresponse = http.request(request)\nputs response.body"}],"security":[{"apiKey":["webhooks:read"]},{"oauth2":["webhooks:read"]}]}},"/webhook-deliveries/{id}/retry":{"post":{"operationId":"webhook_deliveries_retry","tags":["Webhook Deliveries"],"x-fern-sdk-group-name":["webhookDeliveries"],"summary":"Retry a webhook delivery","description":"Queue an immediate retry of this delivery. The delivery is marked `pending`, its attempt count is reset to 0 and `next_retry_at` is set to now, and it is queued for delivery at once. Calling retry on an already-pending delivery queues it again. Failed and dead-lettered deliveries can be retried; a delivery that already succeeded cannot (409 `already_succeeded`).\n\n---\n\n**Related endpoints**\n\n- `GET /webhook-deliveries` — List webhook deliveries\n- `GET /webhook-deliveries/{id}` — Retrieve a webhook delivery\n\n**Common errors**\n\n- `404 resource_missing` — the referenced resource does not exist or is not visible to your key.\n- `409 conflict` — Idempotency-Key collision with a different body, or a concurrent state-transition conflict.\n\n**Idempotency**\n\nPass an `Idempotency-Key` header (UUID v4 recommended) to make retries safe. Keys are valid for 24 hours; see [the idempotency guide](/docs/fundamentals/idempotency).","parameters":[{"schema":{"type":"string","format":"uuid","description":"Webhook delivery ID"},"required":true,"description":"Webhook delivery ID","name":"id","in":"path"}],"responses":{"200":{"description":"Delivery queued for retry.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookDeliveryRetryResponse"}}}},"401":{"description":"Unauthorized - Invalid or missing API key","headers":{"WWW-Authenticate":{"description":"RFC 6750 Bearer authentication challenge","schema":{"type":"string"},"example":"Bearer realm=\"revkeen-api\""}}},"404":{"description":"Webhook delivery not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookDeliveryErrorResponse"}}}},"409":{"description":"Delivery already succeeded and cannot be retried.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookDeliveryErrorResponse"}}}},"429":{"description":"Too Many Requests - Rate limit exceeded","headers":{"Retry-After":{"description":"Whole seconds until the caller may retry","schema":{"type":"string"},"example":"60"},"X-RateLimit-Limit":{"description":"Request quota for the current window","schema":{"type":"string"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window","schema":{"type":"string"}},"X-RateLimit-Reset":{"description":"UNIX timestamp in seconds when the window resets","schema":{"type":"string"}}}}},"x-cli-group":"webhook-deliveries","x-cli-name":"retry","x-codeSamples":[{"lang":"curl","label":"cURL","source":"curl -X POST \"https://api.revkeen.com/v2/webhook-deliveries/11111111-1111-4111-8111-111111111111/retry\" \\\n  -H \"x-api-key: $REVKEEN_API_KEY\""},{"lang":"typescript","label":"TypeScript (SDK)","source":"// npm install @revkeen/sdk@1.20260822.1541\nimport { RevKeenClient } from \"@revkeen/sdk\";\nconst apiKey = process.env.REVKEEN_API_KEY;\nif (!apiKey) throw new Error(\"Set REVKEEN_API_KEY\");\nconst client = new RevKeenClient({ apiKey, baseUrl: \"https://api.revkeen.com/v2\" });\nconst result = await client.webhook.deliveriesRetry(\"11111111-1111-4111-8111-111111111111\");\nconsole.log(result);"},{"lang":"go","label":"Go (HTTP)","source":"package main\nimport (\"fmt\"; \"io\"; \"net/http\"; \"os\"; \"strings\"; \"time\")\nfunc main() {\n  req, err := http.NewRequest(\"POST\", \"https://api.revkeen.com/v2/webhook-deliveries/11111111-1111-4111-8111-111111111111/retry\", strings.NewReader(\"\"))\n  if err != nil { panic(err) }\n  req.Header.Set(\"x-api-key\", os.Getenv(\"REVKEEN_API_KEY\"))\n  req.Header.Set(\"Content-Type\", \"application/json\")\n  client := &http.Client{Timeout: 10 * time.Second}\n  response, err := client.Do(req)\n  if err != nil { panic(err) }\n  defer response.Body.Close()\n  if response.StatusCode >= 400 { panic(fmt.Sprintf(\"RevKeen request failed (%d)\", response.StatusCode)) }\n  data, err := io.ReadAll(response.Body)\n  if err != nil { panic(err) }\n  fmt.Println(string(data))\n}"},{"lang":"php","label":"PHP (HTTP)","source":"<?php\n$request = curl_init(\"https://api.revkeen.com/v2/webhook-deliveries/11111111-1111-4111-8111-111111111111/retry\");\ncurl_setopt_array($request, [\n  CURLOPT_CUSTOMREQUEST => \"POST\",\n  CURLOPT_RETURNTRANSFER => true,\n  CURLOPT_TIMEOUT => 10,\n  CURLOPT_HTTPHEADER => [\"x-api-key: \" . getenv(\"REVKEEN_API_KEY\"), \"Content-Type: application/json\"],\n]);\n$response = curl_exec($request);\nif ($response === false) throw new RuntimeException(curl_error($request));\n$status = curl_getinfo($request, CURLINFO_HTTP_CODE);\ncurl_close($request);\nif ($status >= 400) throw new RuntimeException(\"RevKeen request failed ($status)\");\necho $response;"},{"lang":"python","label":"Python","source":"import os\n\nimport requests\n\nresponse = requests.post(\n    \"https://api.revkeen.com/v2/webhook-deliveries/11111111-1111-4111-8111-111111111111/retry\",\n    headers={\"x-api-key\": os.environ[\"REVKEEN_API_KEY\"]},\n)\n\nresponse.raise_for_status()\nprint(response.json())"},{"lang":"javascript","label":"JavaScript","source":"const credential = process.env.REVKEEN_API_KEY;\nif (!credential) throw new Error(\"Set REVKEEN_API_KEY\");\nconst response = await fetch(\"https://api.revkeen.com/v2/webhook-deliveries/11111111-1111-4111-8111-111111111111/retry\", {\n  method: \"POST\",\n  headers: {\n    \"x-api-key\": credential,\n  },\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"java","label":"Java","source":"import java.net.URI;\nimport java.net.http.HttpClient;\nimport java.net.http.HttpRequest;\nimport java.net.http.HttpResponse;\n\nHttpClient client = HttpClient.newHttpClient();\n\nHttpRequest request = HttpRequest.newBuilder()\n    .uri(URI.create(\"https://api.revkeen.com/v2/webhook-deliveries/11111111-1111-4111-8111-111111111111/retry\"))\n    .header(\"x-api-key\", System.getenv(\"REVKEEN_API_KEY\"))\n    .method(\"POST\", HttpRequest.BodyPublishers.noBody())\n    .build();\n\nHttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());\nSystem.out.println(response.body());"},{"lang":"ruby","label":"Ruby","source":"require \"net/http\"\nrequire \"json\"\nrequire \"uri\"\n\nuri = URI(\"https://api.revkeen.com/v2/webhook-deliveries/11111111-1111-4111-8111-111111111111/retry\")\nhttp = Net::HTTP.new(uri.host, uri.port)\nhttp.use_ssl = true\n\nrequest = Net::HTTP::Post.new(uri)\nrequest[\"x-api-key\"] = ENV[\"REVKEEN_API_KEY\"]\n\nresponse = http.request(request)\nputs response.body"}],"security":[{"apiKey":["webhooks:write"]},{"oauth2":["webhooks:write"]}]}},"/customer-portal/sessions":{"post":{"operationId":"customer_portal_sessions_create","tags":["Customer Portal"],"x-fern-sdk-group-name":["customerPortal","sessions"],"summary":"Create a customer-portal session","description":"Mint a short-lived bearer token that authenticates a specific customer against /v2/customer-portal/*. Returns an opaque `rkcps_...` token that expires in 60 minutes by default. Call this server-side from the merchant's backend, then hand the token to the customer's browser or embedded client. Treat the token like a password — never log it and never expose it to untrusted code.\n\n---\n\n**Related endpoints**\n\n- `GET /customer-portal/customer` — Retrieve the authenticated customer\n- `GET /customer-portal/subscriptions` — List the authenticated customer's subscriptions\n- `GET /customer-portal/subscriptions/{id}` — Retrieve a subscription\n- `POST /customer-portal/subscriptions/{id}/cancel` — Cancel a subscription\n- `GET /customer-portal/invoices` — List the authenticated customer's invoices\n- `GET /customer-portal/invoices/{id}` — Retrieve an invoice\n- `GET /customer-portal/mandates` — List the authenticated customer's Direct Debit mandates\n- `POST /customer-portal/mandates/{id}/re-authorize` — Re-authorise a Direct Debit mandate with new bank details\n\n**Common errors**\n\n- `401 authentication_error` — missing, invalid, expired, or revoked credential. Codes: `authentication_failed`, `invalid_api_key`, `expired_api_key`, `api_key_revoked`, `session_invalid`, `merchant_required`. Carries a `WWW-Authenticate: Bearer` challenge.\n- `404 resource_missing` — the referenced resource does not exist or is not visible to your key.\n\n**Idempotency**\n\nPass an `Idempotency-Key` header (UUID v4 recommended) to make retries safe. Keys are valid for 24 hours; see [the idempotency guide](/docs/fundamentals/idempotency).","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateCustomerPortalSessionRequest"}}}},"responses":{"201":{"description":"Session created. The bearer token is returned only once.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CustomerPortalSessionCreateResponse"}}}},"401":{"description":"Unauthorized - Invalid or missing API key","headers":{"WWW-Authenticate":{"schema":{"type":"string","description":"RFC 6750 Bearer authentication challenge","example":"Bearer realm=\"revkeen-api\""},"required":true,"description":"RFC 6750 Bearer authentication challenge"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthenticationError"}}}},"404":{"description":"Customer not found for this merchant.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CustomerPortalErrorResponse"}}}},"429":{"description":"Too Many Requests - Rate limit exceeded","headers":{"Retry-After":{"description":"Whole seconds until the caller may retry","schema":{"type":"string"},"example":"60"},"X-RateLimit-Limit":{"description":"Request quota for the current window","schema":{"type":"string"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window","schema":{"type":"string"}},"X-RateLimit-Reset":{"description":"UNIX timestamp in seconds when the window resets","schema":{"type":"string"}}}}},"x-cli-group":"customer-portal","x-cli-name":"sessions-create","x-codeSamples":[{"lang":"curl","label":"cURL","source":"curl -X POST \"https://api.revkeen.com/v2/customer-portal/sessions\" \\\n  -H \"x-api-key: $REVKEEN_API_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"customer_id\": \"11111111-1111-4111-8111-111111111111\"\n  }'"},{"lang":"typescript","label":"TypeScript (SDK)","source":"// npm install @revkeen/sdk@1.20260822.1541\nimport { RevKeenClient } from \"@revkeen/sdk\";\nconst apiKey = process.env.REVKEEN_API_KEY;\nif (!apiKey) throw new Error(\"Set REVKEEN_API_KEY\");\nconst client = new RevKeenClient({ apiKey, baseUrl: \"https://api.revkeen.com/v2\" });\nconst result = await client.customer.portalSessionsCreate({\n  \"customer_id\": \"11111111-1111-4111-8111-111111111111\"\n});\nconsole.log(result);"},{"lang":"go","label":"Go (HTTP)","source":"package main\nimport (\"fmt\"; \"io\"; \"net/http\"; \"os\"; \"strings\"; \"time\")\nfunc main() {\n  req, err := http.NewRequest(\"POST\", \"https://api.revkeen.com/v2/customer-portal/sessions\", strings.NewReader(\"{\\\"customer_id\\\":\\\"11111111-1111-4111-8111-111111111111\\\"}\"))\n  if err != nil { panic(err) }\n  req.Header.Set(\"x-api-key\", os.Getenv(\"REVKEEN_API_KEY\"))\n  req.Header.Set(\"Content-Type\", \"application/json\")\n  client := &http.Client{Timeout: 10 * time.Second}\n  response, err := client.Do(req)\n  if err != nil { panic(err) }\n  defer response.Body.Close()\n  if response.StatusCode >= 400 { panic(fmt.Sprintf(\"RevKeen request failed (%d)\", response.StatusCode)) }\n  data, err := io.ReadAll(response.Body)\n  if err != nil { panic(err) }\n  fmt.Println(string(data))\n}"},{"lang":"php","label":"PHP (HTTP)","source":"<?php\n$request = curl_init(\"https://api.revkeen.com/v2/customer-portal/sessions\");\ncurl_setopt_array($request, [\n  CURLOPT_CUSTOMREQUEST => \"POST\",\n  CURLOPT_RETURNTRANSFER => true,\n  CURLOPT_TIMEOUT => 10,\n  CURLOPT_HTTPHEADER => [\"x-api-key: \" . getenv(\"REVKEEN_API_KEY\"), \"Content-Type: application/json\"],\n  CURLOPT_POSTFIELDS => '{\\\"customer_id\\\":\\\"11111111-1111-4111-8111-111111111111\\\"}',\n]);\n$response = curl_exec($request);\nif ($response === false) throw new RuntimeException(curl_error($request));\n$status = curl_getinfo($request, CURLINFO_HTTP_CODE);\ncurl_close($request);\nif ($status >= 400) throw new RuntimeException(\"RevKeen request failed ($status)\");\necho $response;"},{"lang":"python","label":"Python","source":"import os\n\nimport requests\n\nresponse = requests.post(\n    \"https://api.revkeen.com/v2/customer-portal/sessions\",\n    headers={\"x-api-key\": os.environ[\"REVKEEN_API_KEY\"]},\n    json={\n        \"customer_id\": \"11111111-1111-4111-8111-111111111111\",\n    },\n)\n\nresponse.raise_for_status()\nprint(response.json())"},{"lang":"javascript","label":"JavaScript","source":"const credential = process.env.REVKEEN_API_KEY;\nif (!credential) throw new Error(\"Set REVKEEN_API_KEY\");\nconst response = await fetch(\"https://api.revkeen.com/v2/customer-portal/sessions\", {\n  method: \"POST\",\n  headers: {\n    \"x-api-key\": credential,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n    \"customer_id\": \"11111111-1111-4111-8111-111111111111\"\n  }),\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"java","label":"Java","source":"import java.net.URI;\nimport java.net.http.HttpClient;\nimport java.net.http.HttpRequest;\nimport java.net.http.HttpResponse;\n\nHttpClient client = HttpClient.newHttpClient();\n\nHttpRequest request = HttpRequest.newBuilder()\n    .uri(URI.create(\"https://api.revkeen.com/v2/customer-portal/sessions\"))\n    .header(\"x-api-key\", System.getenv(\"REVKEEN_API_KEY\"))\n    .header(\"Content-Type\", \"application/json\")\n    .method(\"POST\", HttpRequest.BodyPublishers.ofString(\"\"\"\n        {\n          \"customer_id\": \"11111111-1111-4111-8111-111111111111\"\n        }\n        \"\"\"))\n    .build();\n\nHttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());\nSystem.out.println(response.body());"},{"lang":"ruby","label":"Ruby","source":"require \"net/http\"\nrequire \"json\"\nrequire \"uri\"\n\nuri = URI(\"https://api.revkeen.com/v2/customer-portal/sessions\")\nhttp = Net::HTTP.new(uri.host, uri.port)\nhttp.use_ssl = true\n\nrequest = Net::HTTP::Post.new(uri)\nrequest[\"x-api-key\"] = ENV[\"REVKEEN_API_KEY\"]\nrequest[\"Content-Type\"] = \"application/json\"\nrequest.body = {\n  \"customer_id\" => \"11111111-1111-4111-8111-111111111111\",\n}.to_json\n\nresponse = http.request(request)\nputs response.body"}],"security":[{"apiKey":["customers:write"]},{"oauth2":["customers:write"]}]}},"/customer-portal/customer":{"get":{"operationId":"customer_portal_customer_get","tags":["Customer Portal"],"x-fern-sdk-group-name":["customerPortal"],"summary":"Retrieve the authenticated customer","description":"Returns the customer represented by the bearer token. Useful as a warm-up / identity check when an embedded portal page loads.\n\n---\n\n**Related endpoints**\n\n- `POST /customer-portal/sessions` — Create a customer-portal session\n- `GET /customer-portal/subscriptions` — List the authenticated customer's subscriptions\n- `GET /customer-portal/subscriptions/{id}` — Retrieve a subscription\n- `POST /customer-portal/subscriptions/{id}/cancel` — Cancel a subscription\n- `GET /customer-portal/invoices` — List the authenticated customer's invoices\n- `GET /customer-portal/invoices/{id}` — Retrieve an invoice\n- `GET /customer-portal/mandates` — List the authenticated customer's Direct Debit mandates\n- `POST /customer-portal/mandates/{id}/re-authorize` — Re-authorise a Direct Debit mandate with new bank details\n\n**Common errors**\n\n- `401 authentication_error` — missing, invalid, expired, or revoked credential. Codes: `authentication_failed`, `invalid_api_key`, `expired_api_key`, `api_key_revoked`, `session_invalid`, `merchant_required`. Carries a `WWW-Authenticate: Bearer` challenge.\n- `404 resource_missing` — the referenced resource does not exist or is not visible to your key.","responses":{"200":{"description":"Authenticated customer.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PortalCustomerResponse"}}}},"401":{"description":"Unauthorized - Invalid or missing API key","headers":{"WWW-Authenticate":{"schema":{"type":"string","description":"RFC 6750 Bearer authentication challenge","example":"Bearer realm=\"revkeen-api\""},"required":true,"description":"RFC 6750 Bearer authentication challenge"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthenticationError"}}}},"404":{"description":"Customer record no longer exists.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CustomerPortalErrorResponse"}}}},"429":{"description":"Too Many Requests - Rate limit exceeded","headers":{"Retry-After":{"description":"Whole seconds until the caller may retry","schema":{"type":"string"},"example":"60"},"X-RateLimit-Limit":{"description":"Request quota for the current window","schema":{"type":"string"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window","schema":{"type":"string"}},"X-RateLimit-Reset":{"description":"UNIX timestamp in seconds when the window resets","schema":{"type":"string"}}}}},"x-cli-group":"customer-portal","x-cli-name":"customer-get","x-codeSamples":[{"lang":"curl","label":"cURL","source":"curl -X GET \"https://api.revkeen.com/v2/customer-portal/customer\" \\\n  -H \"Authorization: Bearer $REVKEEN_PORTAL_TOKEN\""},{"lang":"typescript","label":"TypeScript (HTTP)","source":"const credential = process.env.REVKEEN_PORTAL_TOKEN;\nif (!credential) throw new Error(\"Set REVKEEN_PORTAL_TOKEN\");\nconst response = await fetch(\"https://api.revkeen.com/v2/customer-portal/customer\", {\n  method: \"GET\",\n  headers: {\n    \"Authorization\": `Bearer ${credential}`,\n  },\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"go","label":"Go (HTTP)","source":"package main\nimport (\"fmt\"; \"io\"; \"net/http\"; \"os\"; \"strings\"; \"time\")\nfunc main() {\n  req, err := http.NewRequest(\"GET\", \"https://api.revkeen.com/v2/customer-portal/customer\", strings.NewReader(\"\"))\n  if err != nil { panic(err) }\n  req.Header.Set(\"Authorization\", \"Bearer \" + os.Getenv(\"REVKEEN_PORTAL_TOKEN\"))\n  req.Header.Set(\"Content-Type\", \"application/json\")\n  client := &http.Client{Timeout: 10 * time.Second}\n  response, err := client.Do(req)\n  if err != nil { panic(err) }\n  defer response.Body.Close()\n  if response.StatusCode >= 400 { panic(fmt.Sprintf(\"RevKeen request failed (%d)\", response.StatusCode)) }\n  data, err := io.ReadAll(response.Body)\n  if err != nil { panic(err) }\n  fmt.Println(string(data))\n}"},{"lang":"php","label":"PHP (HTTP)","source":"<?php\n$request = curl_init(\"https://api.revkeen.com/v2/customer-portal/customer\");\ncurl_setopt_array($request, [\n  CURLOPT_CUSTOMREQUEST => \"GET\",\n  CURLOPT_RETURNTRANSFER => true,\n  CURLOPT_TIMEOUT => 10,\n  CURLOPT_HTTPHEADER => [\"Authorization: Bearer \" . getenv(\"REVKEEN_PORTAL_TOKEN\"), \"Content-Type: application/json\"],\n]);\n$response = curl_exec($request);\nif ($response === false) throw new RuntimeException(curl_error($request));\n$status = curl_getinfo($request, CURLINFO_HTTP_CODE);\ncurl_close($request);\nif ($status >= 400) throw new RuntimeException(\"RevKeen request failed ($status)\");\necho $response;"},{"lang":"python","label":"Python","source":"import os\n\nimport requests\n\nresponse = requests.get(\n    \"https://api.revkeen.com/v2/customer-portal/customer\",\n    headers={\"Authorization\": \"Bearer \" + os.environ[\"REVKEEN_PORTAL_TOKEN\"]},\n)\n\nresponse.raise_for_status()\nprint(response.json())"},{"lang":"javascript","label":"JavaScript","source":"const credential = process.env.REVKEEN_PORTAL_TOKEN;\nif (!credential) throw new Error(\"Set REVKEEN_PORTAL_TOKEN\");\nconst response = await fetch(\"https://api.revkeen.com/v2/customer-portal/customer\", {\n  method: \"GET\",\n  headers: {\n    \"Authorization\": `Bearer ${credential}`,\n  },\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"java","label":"Java","source":"import java.net.URI;\nimport java.net.http.HttpClient;\nimport java.net.http.HttpRequest;\nimport java.net.http.HttpResponse;\n\nHttpClient client = HttpClient.newHttpClient();\n\nHttpRequest request = HttpRequest.newBuilder()\n    .uri(URI.create(\"https://api.revkeen.com/v2/customer-portal/customer\"))\n    .header(\"Authorization\", \"Bearer \" + System.getenv(\"REVKEEN_PORTAL_TOKEN\"))\n    .GET()\n    .build();\n\nHttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());\nSystem.out.println(response.body());"},{"lang":"ruby","label":"Ruby","source":"require \"net/http\"\nrequire \"json\"\nrequire \"uri\"\n\nuri = URI(\"https://api.revkeen.com/v2/customer-portal/customer\")\nhttp = Net::HTTP.new(uri.host, uri.port)\nhttp.use_ssl = true\n\nrequest = Net::HTTP::Get.new(uri)\nrequest[\"Authorization\"] = \"Bearer \" + ENV[\"REVKEEN_PORTAL_TOKEN\"]\n\nresponse = http.request(request)\nputs response.body"}]}},"/customer-portal/subscriptions":{"get":{"operationId":"customer_portal_subscriptions_list","tags":["Customer Portal"],"x-fern-sdk-group-name":["customerPortal","subscriptions"],"summary":"List the authenticated customer's subscriptions","description":"Returns subscriptions where the customer is the subscriber. Results are reverse-chronological by creation time and paginate via `starting_after` / `ending_before` cursors.\n\n---\n\n**Related endpoints**\n\n- `POST /customer-portal/sessions` — Create a customer-portal session\n- `GET /customer-portal/customer` — Retrieve the authenticated customer\n- `GET /customer-portal/subscriptions/{id}` — Retrieve a subscription\n- `POST /customer-portal/subscriptions/{id}/cancel` — Cancel a subscription\n- `GET /customer-portal/invoices` — List the authenticated customer's invoices\n- `GET /customer-portal/invoices/{id}` — Retrieve an invoice\n- `GET /customer-portal/mandates` — List the authenticated customer's Direct Debit mandates\n- `POST /customer-portal/mandates/{id}/re-authorize` — Re-authorise a Direct Debit mandate with new bank details\n\n**Common errors**\n\n- `401 authentication_error` — missing, invalid, expired, or revoked credential. Codes: `authentication_failed`, `invalid_api_key`, `expired_api_key`, `api_key_revoked`, `session_invalid`, `merchant_required`. Carries a `WWW-Authenticate: Bearer` challenge.\n\n**Pagination**\n\nOffset-based with `limit` (default 25, max 100) and `offset`. The response `pagination` block includes `total` and `hasMore`. See [the pagination guide](/docs/fundamentals/pagination) for SDK auto-paging helpers.","parameters":[{"schema":{"type":"integer","minimum":1,"maximum":100,"default":20,"description":"Maximum number of results to return (1-100, default 20)."},"required":false,"description":"Maximum number of results to return (1-100, default 20).","name":"limit","in":"query"},{"schema":{"type":"string","format":"uuid","description":"Cursor — return results created before the row with this ID."},"required":false,"description":"Cursor — return results created before the row with this ID.","name":"starting_after","in":"query"},{"schema":{"type":"string","format":"uuid","description":"Cursor — return results created after the row with this ID."},"required":false,"description":"Cursor — return results created after the row with this ID.","name":"ending_before","in":"query"}],"responses":{"200":{"description":"Paginated list of subscriptions.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PortalSubscriptionList"}}}},"401":{"description":"Unauthorized - Invalid or missing API key","headers":{"WWW-Authenticate":{"schema":{"type":"string","description":"RFC 6750 Bearer authentication challenge","example":"Bearer realm=\"revkeen-api\""},"required":true,"description":"RFC 6750 Bearer authentication challenge"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthenticationError"}}}},"429":{"description":"Too Many Requests - Rate limit exceeded","headers":{"Retry-After":{"description":"Whole seconds until the caller may retry","schema":{"type":"string"},"example":"60"},"X-RateLimit-Limit":{"description":"Request quota for the current window","schema":{"type":"string"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window","schema":{"type":"string"}},"X-RateLimit-Reset":{"description":"UNIX timestamp in seconds when the window resets","schema":{"type":"string"}}}}},"x-cli-group":"customer-portal","x-cli-name":"subscriptions-list","x-codeSamples":[{"lang":"curl","label":"cURL","source":"curl -X GET \"https://api.revkeen.com/v2/customer-portal/subscriptions?limit=20&starting_after=11111111-1111-4111-8111-111111111111\" \\\n  -H \"Authorization: Bearer $REVKEEN_PORTAL_TOKEN\""},{"lang":"typescript","label":"TypeScript (HTTP)","source":"const credential = process.env.REVKEEN_PORTAL_TOKEN;\nif (!credential) throw new Error(\"Set REVKEEN_PORTAL_TOKEN\");\nconst response = await fetch(\"https://api.revkeen.com/v2/customer-portal/subscriptions?limit=20&starting_after=11111111-1111-4111-8111-111111111111\", {\n  method: \"GET\",\n  headers: {\n    \"Authorization\": `Bearer ${credential}`,\n  },\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"go","label":"Go (HTTP)","source":"package main\nimport (\"fmt\"; \"io\"; \"net/http\"; \"os\"; \"strings\"; \"time\")\nfunc main() {\n  req, err := http.NewRequest(\"GET\", \"https://api.revkeen.com/v2/customer-portal/subscriptions?limit=20&starting_after=11111111-1111-4111-8111-111111111111\", strings.NewReader(\"\"))\n  if err != nil { panic(err) }\n  req.Header.Set(\"Authorization\", \"Bearer \" + os.Getenv(\"REVKEEN_PORTAL_TOKEN\"))\n  req.Header.Set(\"Content-Type\", \"application/json\")\n  client := &http.Client{Timeout: 10 * time.Second}\n  response, err := client.Do(req)\n  if err != nil { panic(err) }\n  defer response.Body.Close()\n  if response.StatusCode >= 400 { panic(fmt.Sprintf(\"RevKeen request failed (%d)\", response.StatusCode)) }\n  data, err := io.ReadAll(response.Body)\n  if err != nil { panic(err) }\n  fmt.Println(string(data))\n}"},{"lang":"php","label":"PHP (HTTP)","source":"<?php\n$request = curl_init(\"https://api.revkeen.com/v2/customer-portal/subscriptions?limit=20&starting_after=11111111-1111-4111-8111-111111111111\");\ncurl_setopt_array($request, [\n  CURLOPT_CUSTOMREQUEST => \"GET\",\n  CURLOPT_RETURNTRANSFER => true,\n  CURLOPT_TIMEOUT => 10,\n  CURLOPT_HTTPHEADER => [\"Authorization: Bearer \" . getenv(\"REVKEEN_PORTAL_TOKEN\"), \"Content-Type: application/json\"],\n]);\n$response = curl_exec($request);\nif ($response === false) throw new RuntimeException(curl_error($request));\n$status = curl_getinfo($request, CURLINFO_HTTP_CODE);\ncurl_close($request);\nif ($status >= 400) throw new RuntimeException(\"RevKeen request failed ($status)\");\necho $response;"},{"lang":"python","label":"Python","source":"import os\n\nimport requests\n\nresponse = requests.get(\n    \"https://api.revkeen.com/v2/customer-portal/subscriptions\",\n    headers={\"Authorization\": \"Bearer \" + os.environ[\"REVKEEN_PORTAL_TOKEN\"]},\n    params={\n        \"limit\": 20,\n        \"starting_after\": \"11111111-1111-4111-8111-111111111111\",\n    },\n)\n\nresponse.raise_for_status()\nprint(response.json())"},{"lang":"javascript","label":"JavaScript","source":"const credential = process.env.REVKEEN_PORTAL_TOKEN;\nif (!credential) throw new Error(\"Set REVKEEN_PORTAL_TOKEN\");\nconst response = await fetch(\"https://api.revkeen.com/v2/customer-portal/subscriptions?limit=20&starting_after=11111111-1111-4111-8111-111111111111\", {\n  method: \"GET\",\n  headers: {\n    \"Authorization\": `Bearer ${credential}`,\n  },\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"java","label":"Java","source":"import java.net.URI;\nimport java.net.http.HttpClient;\nimport java.net.http.HttpRequest;\nimport java.net.http.HttpResponse;\n\nHttpClient client = HttpClient.newHttpClient();\n\nHttpRequest request = HttpRequest.newBuilder()\n    .uri(URI.create(\"https://api.revkeen.com/v2/customer-portal/subscriptions?limit=20&starting_after=11111111-1111-4111-8111-111111111111\"))\n    .header(\"Authorization\", \"Bearer \" + System.getenv(\"REVKEEN_PORTAL_TOKEN\"))\n    .GET()\n    .build();\n\nHttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());\nSystem.out.println(response.body());"},{"lang":"ruby","label":"Ruby","source":"require \"net/http\"\nrequire \"json\"\nrequire \"uri\"\n\nuri = URI(\"https://api.revkeen.com/v2/customer-portal/subscriptions?limit=20&starting_after=11111111-1111-4111-8111-111111111111\")\nhttp = Net::HTTP.new(uri.host, uri.port)\nhttp.use_ssl = true\n\nrequest = Net::HTTP::Get.new(uri)\nrequest[\"Authorization\"] = \"Bearer \" + ENV[\"REVKEEN_PORTAL_TOKEN\"]\n\nresponse = http.request(request)\nputs response.body"}]}},"/customer-portal/subscriptions/{id}":{"get":{"operationId":"customer_portal_subscriptions_get","tags":["Customer Portal"],"x-fern-sdk-group-name":["customerPortal","subscriptions"],"summary":"Retrieve a subscription","description":"Returns a subscription owned by the authenticated customer. 404s on cross-customer access even if the subscription exists.\n\n---\n\n**Related endpoints**\n\n- `POST /customer-portal/sessions` — Create a customer-portal session\n- `GET /customer-portal/customer` — Retrieve the authenticated customer\n- `GET /customer-portal/subscriptions` — List the authenticated customer's subscriptions\n- `POST /customer-portal/subscriptions/{id}/cancel` — Cancel a subscription\n- `GET /customer-portal/invoices` — List the authenticated customer's invoices\n- `GET /customer-portal/invoices/{id}` — Retrieve an invoice\n- `GET /customer-portal/mandates` — List the authenticated customer's Direct Debit mandates\n- `POST /customer-portal/mandates/{id}/re-authorize` — Re-authorise a Direct Debit mandate with new bank details\n\n**Common errors**\n\n- `401 authentication_error` — missing, invalid, expired, or revoked credential. Codes: `authentication_failed`, `invalid_api_key`, `expired_api_key`, `api_key_revoked`, `session_invalid`, `merchant_required`. Carries a `WWW-Authenticate: Bearer` challenge.\n- `404 resource_missing` — the referenced resource does not exist or is not visible to your key.","parameters":[{"schema":{"type":"string","format":"uuid","description":"Subscription ID."},"required":true,"description":"Subscription ID.","name":"id","in":"path"}],"responses":{"200":{"description":"Subscription details.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PortalSubscriptionResponse"}}}},"401":{"description":"Unauthorized - Invalid or missing API key","headers":{"WWW-Authenticate":{"schema":{"type":"string","description":"RFC 6750 Bearer authentication challenge","example":"Bearer realm=\"revkeen-api\""},"required":true,"description":"RFC 6750 Bearer authentication challenge"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthenticationError"}}}},"404":{"description":"Subscription not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CustomerPortalErrorResponse"}}}},"429":{"description":"Too Many Requests - Rate limit exceeded","headers":{"Retry-After":{"description":"Whole seconds until the caller may retry","schema":{"type":"string"},"example":"60"},"X-RateLimit-Limit":{"description":"Request quota for the current window","schema":{"type":"string"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window","schema":{"type":"string"}},"X-RateLimit-Reset":{"description":"UNIX timestamp in seconds when the window resets","schema":{"type":"string"}}}}},"x-cli-group":"customer-portal","x-cli-name":"subscriptions-get","x-codeSamples":[{"lang":"curl","label":"cURL","source":"curl -X GET \"https://api.revkeen.com/v2/customer-portal/subscriptions/11111111-1111-4111-8111-111111111111\" \\\n  -H \"Authorization: Bearer $REVKEEN_PORTAL_TOKEN\""},{"lang":"typescript","label":"TypeScript (HTTP)","source":"const credential = process.env.REVKEEN_PORTAL_TOKEN;\nif (!credential) throw new Error(\"Set REVKEEN_PORTAL_TOKEN\");\nconst response = await fetch(\"https://api.revkeen.com/v2/customer-portal/subscriptions/11111111-1111-4111-8111-111111111111\", {\n  method: \"GET\",\n  headers: {\n    \"Authorization\": `Bearer ${credential}`,\n  },\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"go","label":"Go (HTTP)","source":"package main\nimport (\"fmt\"; \"io\"; \"net/http\"; \"os\"; \"strings\"; \"time\")\nfunc main() {\n  req, err := http.NewRequest(\"GET\", \"https://api.revkeen.com/v2/customer-portal/subscriptions/11111111-1111-4111-8111-111111111111\", strings.NewReader(\"\"))\n  if err != nil { panic(err) }\n  req.Header.Set(\"Authorization\", \"Bearer \" + os.Getenv(\"REVKEEN_PORTAL_TOKEN\"))\n  req.Header.Set(\"Content-Type\", \"application/json\")\n  client := &http.Client{Timeout: 10 * time.Second}\n  response, err := client.Do(req)\n  if err != nil { panic(err) }\n  defer response.Body.Close()\n  if response.StatusCode >= 400 { panic(fmt.Sprintf(\"RevKeen request failed (%d)\", response.StatusCode)) }\n  data, err := io.ReadAll(response.Body)\n  if err != nil { panic(err) }\n  fmt.Println(string(data))\n}"},{"lang":"php","label":"PHP (HTTP)","source":"<?php\n$request = curl_init(\"https://api.revkeen.com/v2/customer-portal/subscriptions/11111111-1111-4111-8111-111111111111\");\ncurl_setopt_array($request, [\n  CURLOPT_CUSTOMREQUEST => \"GET\",\n  CURLOPT_RETURNTRANSFER => true,\n  CURLOPT_TIMEOUT => 10,\n  CURLOPT_HTTPHEADER => [\"Authorization: Bearer \" . getenv(\"REVKEEN_PORTAL_TOKEN\"), \"Content-Type: application/json\"],\n]);\n$response = curl_exec($request);\nif ($response === false) throw new RuntimeException(curl_error($request));\n$status = curl_getinfo($request, CURLINFO_HTTP_CODE);\ncurl_close($request);\nif ($status >= 400) throw new RuntimeException(\"RevKeen request failed ($status)\");\necho $response;"},{"lang":"python","label":"Python","source":"import os\n\nimport requests\n\nresponse = requests.get(\n    \"https://api.revkeen.com/v2/customer-portal/subscriptions/11111111-1111-4111-8111-111111111111\",\n    headers={\"Authorization\": \"Bearer \" + os.environ[\"REVKEEN_PORTAL_TOKEN\"]},\n)\n\nresponse.raise_for_status()\nprint(response.json())"},{"lang":"javascript","label":"JavaScript","source":"const credential = process.env.REVKEEN_PORTAL_TOKEN;\nif (!credential) throw new Error(\"Set REVKEEN_PORTAL_TOKEN\");\nconst response = await fetch(\"https://api.revkeen.com/v2/customer-portal/subscriptions/11111111-1111-4111-8111-111111111111\", {\n  method: \"GET\",\n  headers: {\n    \"Authorization\": `Bearer ${credential}`,\n  },\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"java","label":"Java","source":"import java.net.URI;\nimport java.net.http.HttpClient;\nimport java.net.http.HttpRequest;\nimport java.net.http.HttpResponse;\n\nHttpClient client = HttpClient.newHttpClient();\n\nHttpRequest request = HttpRequest.newBuilder()\n    .uri(URI.create(\"https://api.revkeen.com/v2/customer-portal/subscriptions/11111111-1111-4111-8111-111111111111\"))\n    .header(\"Authorization\", \"Bearer \" + System.getenv(\"REVKEEN_PORTAL_TOKEN\"))\n    .GET()\n    .build();\n\nHttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());\nSystem.out.println(response.body());"},{"lang":"ruby","label":"Ruby","source":"require \"net/http\"\nrequire \"json\"\nrequire \"uri\"\n\nuri = URI(\"https://api.revkeen.com/v2/customer-portal/subscriptions/11111111-1111-4111-8111-111111111111\")\nhttp = Net::HTTP.new(uri.host, uri.port)\nhttp.use_ssl = true\n\nrequest = Net::HTTP::Get.new(uri)\nrequest[\"Authorization\"] = \"Bearer \" + ENV[\"REVKEEN_PORTAL_TOKEN\"]\n\nresponse = http.request(request)\nputs response.body"}]}},"/customer-portal/subscriptions/{id}/cancel":{"post":{"operationId":"customer_portal_subscriptions_cancel","tags":["Customer Portal"],"x-fern-sdk-group-name":["customerPortal","subscriptions"],"summary":"Cancel a subscription","description":"Cancel a subscription owned by the authenticated customer. By default the subscription is scheduled to cancel at the end of the current billing period — set `cancel_at_period_end=false` to cancel immediately. Idempotent — cancelling an already-canceled subscription is a no-op that returns the current state.\n\n---\n\n**Related endpoints**\n\n- `POST /customer-portal/sessions` — Create a customer-portal session\n- `GET /customer-portal/customer` — Retrieve the authenticated customer\n- `GET /customer-portal/subscriptions` — List the authenticated customer's subscriptions\n- `GET /customer-portal/subscriptions/{id}` — Retrieve a subscription\n- `GET /customer-portal/invoices` — List the authenticated customer's invoices\n- `GET /customer-portal/invoices/{id}` — Retrieve an invoice\n- `GET /customer-portal/mandates` — List the authenticated customer's Direct Debit mandates\n- `POST /customer-portal/mandates/{id}/re-authorize` — Re-authorise a Direct Debit mandate with new bank details\n\n**Common errors**\n\n- `401 authentication_error` — missing, invalid, expired, or revoked credential. Codes: `authentication_failed`, `invalid_api_key`, `expired_api_key`, `api_key_revoked`, `session_invalid`, `merchant_required`. Carries a `WWW-Authenticate: Bearer` challenge.\n- `404 resource_missing` — the referenced resource does not exist or is not visible to your key.\n\n**Idempotency**\n\nPass an `Idempotency-Key` header (UUID v4 recommended) to make retries safe. Keys are valid for 24 hours; see [the idempotency guide](/docs/fundamentals/idempotency).","parameters":[{"schema":{"type":"string","format":"uuid","description":"Subscription ID to cancel."},"required":true,"description":"Subscription ID to cancel.","name":"id","in":"path"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CancelSubscriptionRequest"}}}},"responses":{"200":{"description":"Subscription canceled.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PortalSubscriptionCancelResponse"}}}},"401":{"description":"Unauthorized - Invalid or missing API key","headers":{"WWW-Authenticate":{"schema":{"type":"string","description":"RFC 6750 Bearer authentication challenge","example":"Bearer realm=\"revkeen-api\""},"required":true,"description":"RFC 6750 Bearer authentication challenge"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthenticationError"}}}},"404":{"description":"Subscription not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CustomerPortalErrorResponse"}}}},"429":{"description":"Too Many Requests - Rate limit exceeded","headers":{"Retry-After":{"description":"Whole seconds until the caller may retry","schema":{"type":"string"},"example":"60"},"X-RateLimit-Limit":{"description":"Request quota for the current window","schema":{"type":"string"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window","schema":{"type":"string"}},"X-RateLimit-Reset":{"description":"UNIX timestamp in seconds when the window resets","schema":{"type":"string"}}}}},"x-cli-group":"customer-portal","x-cli-name":"subscriptions-cancel","x-codeSamples":[{"lang":"curl","label":"cURL","source":"curl -X POST \"https://api.revkeen.com/v2/customer-portal/subscriptions/11111111-1111-4111-8111-111111111111/cancel\" \\\n  -H \"Authorization: Bearer $REVKEEN_PORTAL_TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"cancel_at_period_end\": true,\n    \"reason\": \"example\"\n  }'"},{"lang":"typescript","label":"TypeScript (HTTP)","source":"const credential = process.env.REVKEEN_PORTAL_TOKEN;\nif (!credential) throw new Error(\"Set REVKEEN_PORTAL_TOKEN\");\nconst response = await fetch(\"https://api.revkeen.com/v2/customer-portal/subscriptions/11111111-1111-4111-8111-111111111111/cancel\", {\n  method: \"POST\",\n  headers: {\n    \"Authorization\": `Bearer ${credential}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n    \"cancel_at_period_end\": true,\n    \"reason\": \"example\"\n  }),\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"go","label":"Go (HTTP)","source":"package main\nimport (\"fmt\"; \"io\"; \"net/http\"; \"os\"; \"strings\"; \"time\")\nfunc main() {\n  req, err := http.NewRequest(\"POST\", \"https://api.revkeen.com/v2/customer-portal/subscriptions/11111111-1111-4111-8111-111111111111/cancel\", strings.NewReader(\"{\\\"cancel_at_period_end\\\":true,\\\"reason\\\":\\\"example\\\"}\"))\n  if err != nil { panic(err) }\n  req.Header.Set(\"Authorization\", \"Bearer \" + os.Getenv(\"REVKEEN_PORTAL_TOKEN\"))\n  req.Header.Set(\"Content-Type\", \"application/json\")\n  client := &http.Client{Timeout: 10 * time.Second}\n  response, err := client.Do(req)\n  if err != nil { panic(err) }\n  defer response.Body.Close()\n  if response.StatusCode >= 400 { panic(fmt.Sprintf(\"RevKeen request failed (%d)\", response.StatusCode)) }\n  data, err := io.ReadAll(response.Body)\n  if err != nil { panic(err) }\n  fmt.Println(string(data))\n}"},{"lang":"php","label":"PHP (HTTP)","source":"<?php\n$request = curl_init(\"https://api.revkeen.com/v2/customer-portal/subscriptions/11111111-1111-4111-8111-111111111111/cancel\");\ncurl_setopt_array($request, [\n  CURLOPT_CUSTOMREQUEST => \"POST\",\n  CURLOPT_RETURNTRANSFER => true,\n  CURLOPT_TIMEOUT => 10,\n  CURLOPT_HTTPHEADER => [\"Authorization: Bearer \" . getenv(\"REVKEEN_PORTAL_TOKEN\"), \"Content-Type: application/json\"],\n  CURLOPT_POSTFIELDS => '{\\\"cancel_at_period_end\\\":true,\\\"reason\\\":\\\"example\\\"}',\n]);\n$response = curl_exec($request);\nif ($response === false) throw new RuntimeException(curl_error($request));\n$status = curl_getinfo($request, CURLINFO_HTTP_CODE);\ncurl_close($request);\nif ($status >= 400) throw new RuntimeException(\"RevKeen request failed ($status)\");\necho $response;"},{"lang":"python","label":"Python","source":"import os\n\nimport requests\n\nresponse = requests.post(\n    \"https://api.revkeen.com/v2/customer-portal/subscriptions/11111111-1111-4111-8111-111111111111/cancel\",\n    headers={\"Authorization\": \"Bearer \" + os.environ[\"REVKEEN_PORTAL_TOKEN\"]},\n    json={\n        \"cancel_at_period_end\": True,\n        \"reason\": \"example\",\n    },\n)\n\nresponse.raise_for_status()\nprint(response.json())"},{"lang":"javascript","label":"JavaScript","source":"const credential = process.env.REVKEEN_PORTAL_TOKEN;\nif (!credential) throw new Error(\"Set REVKEEN_PORTAL_TOKEN\");\nconst response = await fetch(\"https://api.revkeen.com/v2/customer-portal/subscriptions/11111111-1111-4111-8111-111111111111/cancel\", {\n  method: \"POST\",\n  headers: {\n    \"Authorization\": `Bearer ${credential}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n    \"cancel_at_period_end\": true,\n    \"reason\": \"example\"\n  }),\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"java","label":"Java","source":"import java.net.URI;\nimport java.net.http.HttpClient;\nimport java.net.http.HttpRequest;\nimport java.net.http.HttpResponse;\n\nHttpClient client = HttpClient.newHttpClient();\n\nHttpRequest request = HttpRequest.newBuilder()\n    .uri(URI.create(\"https://api.revkeen.com/v2/customer-portal/subscriptions/11111111-1111-4111-8111-111111111111/cancel\"))\n    .header(\"Authorization\", \"Bearer \" + System.getenv(\"REVKEEN_PORTAL_TOKEN\"))\n    .header(\"Content-Type\", \"application/json\")\n    .method(\"POST\", HttpRequest.BodyPublishers.ofString(\"\"\"\n        {\n          \"cancel_at_period_end\": true,\n          \"reason\": \"example\"\n        }\n        \"\"\"))\n    .build();\n\nHttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());\nSystem.out.println(response.body());"},{"lang":"ruby","label":"Ruby","source":"require \"net/http\"\nrequire \"json\"\nrequire \"uri\"\n\nuri = URI(\"https://api.revkeen.com/v2/customer-portal/subscriptions/11111111-1111-4111-8111-111111111111/cancel\")\nhttp = Net::HTTP.new(uri.host, uri.port)\nhttp.use_ssl = true\n\nrequest = Net::HTTP::Post.new(uri)\nrequest[\"Authorization\"] = \"Bearer \" + ENV[\"REVKEEN_PORTAL_TOKEN\"]\nrequest[\"Content-Type\"] = \"application/json\"\nrequest.body = {\n  \"cancel_at_period_end\" => true,\n  \"reason\" => \"example\",\n}.to_json\n\nresponse = http.request(request)\nputs response.body"}]}},"/customer-portal/invoices":{"get":{"operationId":"customer_portal_invoices_list","tags":["Customer Portal"],"x-fern-sdk-group-name":["customerPortal","invoices"],"summary":"List the authenticated customer's invoices","description":"Returns invoices owned by the authenticated customer, reverse-chronological by `invoice_date`. Use the same `starting_after`/`ending_before` cursor pattern as subscriptions.\n\n---\n\n**Related endpoints**\n\n- `POST /customer-portal/sessions` — Create a customer-portal session\n- `GET /customer-portal/customer` — Retrieve the authenticated customer\n- `GET /customer-portal/subscriptions` — List the authenticated customer's subscriptions\n- `GET /customer-portal/subscriptions/{id}` — Retrieve a subscription\n- `POST /customer-portal/subscriptions/{id}/cancel` — Cancel a subscription\n- `GET /customer-portal/invoices/{id}` — Retrieve an invoice\n- `GET /customer-portal/mandates` — List the authenticated customer's Direct Debit mandates\n- `POST /customer-portal/mandates/{id}/re-authorize` — Re-authorise a Direct Debit mandate with new bank details\n\n**Common errors**\n\n- `401 authentication_error` — missing, invalid, expired, or revoked credential. Codes: `authentication_failed`, `invalid_api_key`, `expired_api_key`, `api_key_revoked`, `session_invalid`, `merchant_required`. Carries a `WWW-Authenticate: Bearer` challenge.\n\n**Pagination**\n\nOffset-based with `limit` (default 25, max 100) and `offset`. The response `pagination` block includes `total` and `hasMore`. See [the pagination guide](/docs/fundamentals/pagination) for SDK auto-paging helpers.","parameters":[{"schema":{"type":"integer","minimum":1,"maximum":100,"default":20,"description":"Maximum number of results to return (1-100, default 20)."},"required":false,"description":"Maximum number of results to return (1-100, default 20).","name":"limit","in":"query"},{"schema":{"type":"string","format":"uuid","description":"Cursor — return results created before the row with this ID."},"required":false,"description":"Cursor — return results created before the row with this ID.","name":"starting_after","in":"query"},{"schema":{"type":"string","format":"uuid","description":"Cursor — return results created after the row with this ID."},"required":false,"description":"Cursor — return results created after the row with this ID.","name":"ending_before","in":"query"}],"responses":{"200":{"description":"Paginated list of invoices.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PortalInvoiceList"}}}},"401":{"description":"Unauthorized - Invalid or missing API key","headers":{"WWW-Authenticate":{"schema":{"type":"string","description":"RFC 6750 Bearer authentication challenge","example":"Bearer realm=\"revkeen-api\""},"required":true,"description":"RFC 6750 Bearer authentication challenge"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthenticationError"}}}},"429":{"description":"Too Many Requests - Rate limit exceeded","headers":{"Retry-After":{"description":"Whole seconds until the caller may retry","schema":{"type":"string"},"example":"60"},"X-RateLimit-Limit":{"description":"Request quota for the current window","schema":{"type":"string"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window","schema":{"type":"string"}},"X-RateLimit-Reset":{"description":"UNIX timestamp in seconds when the window resets","schema":{"type":"string"}}}}},"x-cli-group":"customer-portal","x-cli-name":"invoices-list","x-codeSamples":[{"lang":"curl","label":"cURL","source":"curl -X GET \"https://api.revkeen.com/v2/customer-portal/invoices?limit=20&starting_after=11111111-1111-4111-8111-111111111111\" \\\n  -H \"Authorization: Bearer $REVKEEN_PORTAL_TOKEN\""},{"lang":"typescript","label":"TypeScript (HTTP)","source":"const credential = process.env.REVKEEN_PORTAL_TOKEN;\nif (!credential) throw new Error(\"Set REVKEEN_PORTAL_TOKEN\");\nconst response = await fetch(\"https://api.revkeen.com/v2/customer-portal/invoices?limit=20&starting_after=11111111-1111-4111-8111-111111111111\", {\n  method: \"GET\",\n  headers: {\n    \"Authorization\": `Bearer ${credential}`,\n  },\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"go","label":"Go (HTTP)","source":"package main\nimport (\"fmt\"; \"io\"; \"net/http\"; \"os\"; \"strings\"; \"time\")\nfunc main() {\n  req, err := http.NewRequest(\"GET\", \"https://api.revkeen.com/v2/customer-portal/invoices?limit=20&starting_after=11111111-1111-4111-8111-111111111111\", strings.NewReader(\"\"))\n  if err != nil { panic(err) }\n  req.Header.Set(\"Authorization\", \"Bearer \" + os.Getenv(\"REVKEEN_PORTAL_TOKEN\"))\n  req.Header.Set(\"Content-Type\", \"application/json\")\n  client := &http.Client{Timeout: 10 * time.Second}\n  response, err := client.Do(req)\n  if err != nil { panic(err) }\n  defer response.Body.Close()\n  if response.StatusCode >= 400 { panic(fmt.Sprintf(\"RevKeen request failed (%d)\", response.StatusCode)) }\n  data, err := io.ReadAll(response.Body)\n  if err != nil { panic(err) }\n  fmt.Println(string(data))\n}"},{"lang":"php","label":"PHP (HTTP)","source":"<?php\n$request = curl_init(\"https://api.revkeen.com/v2/customer-portal/invoices?limit=20&starting_after=11111111-1111-4111-8111-111111111111\");\ncurl_setopt_array($request, [\n  CURLOPT_CUSTOMREQUEST => \"GET\",\n  CURLOPT_RETURNTRANSFER => true,\n  CURLOPT_TIMEOUT => 10,\n  CURLOPT_HTTPHEADER => [\"Authorization: Bearer \" . getenv(\"REVKEEN_PORTAL_TOKEN\"), \"Content-Type: application/json\"],\n]);\n$response = curl_exec($request);\nif ($response === false) throw new RuntimeException(curl_error($request));\n$status = curl_getinfo($request, CURLINFO_HTTP_CODE);\ncurl_close($request);\nif ($status >= 400) throw new RuntimeException(\"RevKeen request failed ($status)\");\necho $response;"},{"lang":"python","label":"Python","source":"import os\n\nimport requests\n\nresponse = requests.get(\n    \"https://api.revkeen.com/v2/customer-portal/invoices\",\n    headers={\"Authorization\": \"Bearer \" + os.environ[\"REVKEEN_PORTAL_TOKEN\"]},\n    params={\n        \"limit\": 20,\n        \"starting_after\": \"11111111-1111-4111-8111-111111111111\",\n    },\n)\n\nresponse.raise_for_status()\nprint(response.json())"},{"lang":"javascript","label":"JavaScript","source":"const credential = process.env.REVKEEN_PORTAL_TOKEN;\nif (!credential) throw new Error(\"Set REVKEEN_PORTAL_TOKEN\");\nconst response = await fetch(\"https://api.revkeen.com/v2/customer-portal/invoices?limit=20&starting_after=11111111-1111-4111-8111-111111111111\", {\n  method: \"GET\",\n  headers: {\n    \"Authorization\": `Bearer ${credential}`,\n  },\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"java","label":"Java","source":"import java.net.URI;\nimport java.net.http.HttpClient;\nimport java.net.http.HttpRequest;\nimport java.net.http.HttpResponse;\n\nHttpClient client = HttpClient.newHttpClient();\n\nHttpRequest request = HttpRequest.newBuilder()\n    .uri(URI.create(\"https://api.revkeen.com/v2/customer-portal/invoices?limit=20&starting_after=11111111-1111-4111-8111-111111111111\"))\n    .header(\"Authorization\", \"Bearer \" + System.getenv(\"REVKEEN_PORTAL_TOKEN\"))\n    .GET()\n    .build();\n\nHttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());\nSystem.out.println(response.body());"},{"lang":"ruby","label":"Ruby","source":"require \"net/http\"\nrequire \"json\"\nrequire \"uri\"\n\nuri = URI(\"https://api.revkeen.com/v2/customer-portal/invoices?limit=20&starting_after=11111111-1111-4111-8111-111111111111\")\nhttp = Net::HTTP.new(uri.host, uri.port)\nhttp.use_ssl = true\n\nrequest = Net::HTTP::Get.new(uri)\nrequest[\"Authorization\"] = \"Bearer \" + ENV[\"REVKEEN_PORTAL_TOKEN\"]\n\nresponse = http.request(request)\nputs response.body"}]}},"/customer-portal/invoices/{id}":{"get":{"operationId":"customer_portal_invoices_get","tags":["Customer Portal"],"x-fern-sdk-group-name":["customerPortal","invoices"],"summary":"Retrieve an invoice","description":"Returns an invoice owned by the authenticated customer.\n\n---\n\n**Related endpoints**\n\n- `POST /customer-portal/sessions` — Create a customer-portal session\n- `GET /customer-portal/customer` — Retrieve the authenticated customer\n- `GET /customer-portal/subscriptions` — List the authenticated customer's subscriptions\n- `GET /customer-portal/subscriptions/{id}` — Retrieve a subscription\n- `POST /customer-portal/subscriptions/{id}/cancel` — Cancel a subscription\n- `GET /customer-portal/invoices` — List the authenticated customer's invoices\n- `GET /customer-portal/mandates` — List the authenticated customer's Direct Debit mandates\n- `POST /customer-portal/mandates/{id}/re-authorize` — Re-authorise a Direct Debit mandate with new bank details\n\n**Common errors**\n\n- `401 authentication_error` — missing, invalid, expired, or revoked credential. Codes: `authentication_failed`, `invalid_api_key`, `expired_api_key`, `api_key_revoked`, `session_invalid`, `merchant_required`. Carries a `WWW-Authenticate: Bearer` challenge.\n- `404 resource_missing` — the referenced resource does not exist or is not visible to your key.","parameters":[{"schema":{"type":"string","format":"uuid","description":"Invoice ID."},"required":true,"description":"Invoice ID.","name":"id","in":"path"}],"responses":{"200":{"description":"Invoice details.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PortalInvoiceResponse"}}}},"401":{"description":"Unauthorized - Invalid or missing API key","headers":{"WWW-Authenticate":{"schema":{"type":"string","description":"RFC 6750 Bearer authentication challenge","example":"Bearer realm=\"revkeen-api\""},"required":true,"description":"RFC 6750 Bearer authentication challenge"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthenticationError"}}}},"404":{"description":"Invoice not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CustomerPortalErrorResponse"}}}},"429":{"description":"Too Many Requests - Rate limit exceeded","headers":{"Retry-After":{"description":"Whole seconds until the caller may retry","schema":{"type":"string"},"example":"60"},"X-RateLimit-Limit":{"description":"Request quota for the current window","schema":{"type":"string"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window","schema":{"type":"string"}},"X-RateLimit-Reset":{"description":"UNIX timestamp in seconds when the window resets","schema":{"type":"string"}}}}},"x-cli-group":"customer-portal","x-cli-name":"invoices-get","x-codeSamples":[{"lang":"curl","label":"cURL","source":"curl -X GET \"https://api.revkeen.com/v2/customer-portal/invoices/11111111-1111-4111-8111-111111111111\" \\\n  -H \"Authorization: Bearer $REVKEEN_PORTAL_TOKEN\""},{"lang":"typescript","label":"TypeScript (HTTP)","source":"const credential = process.env.REVKEEN_PORTAL_TOKEN;\nif (!credential) throw new Error(\"Set REVKEEN_PORTAL_TOKEN\");\nconst response = await fetch(\"https://api.revkeen.com/v2/customer-portal/invoices/11111111-1111-4111-8111-111111111111\", {\n  method: \"GET\",\n  headers: {\n    \"Authorization\": `Bearer ${credential}`,\n  },\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"go","label":"Go (HTTP)","source":"package main\nimport (\"fmt\"; \"io\"; \"net/http\"; \"os\"; \"strings\"; \"time\")\nfunc main() {\n  req, err := http.NewRequest(\"GET\", \"https://api.revkeen.com/v2/customer-portal/invoices/11111111-1111-4111-8111-111111111111\", strings.NewReader(\"\"))\n  if err != nil { panic(err) }\n  req.Header.Set(\"Authorization\", \"Bearer \" + os.Getenv(\"REVKEEN_PORTAL_TOKEN\"))\n  req.Header.Set(\"Content-Type\", \"application/json\")\n  client := &http.Client{Timeout: 10 * time.Second}\n  response, err := client.Do(req)\n  if err != nil { panic(err) }\n  defer response.Body.Close()\n  if response.StatusCode >= 400 { panic(fmt.Sprintf(\"RevKeen request failed (%d)\", response.StatusCode)) }\n  data, err := io.ReadAll(response.Body)\n  if err != nil { panic(err) }\n  fmt.Println(string(data))\n}"},{"lang":"php","label":"PHP (HTTP)","source":"<?php\n$request = curl_init(\"https://api.revkeen.com/v2/customer-portal/invoices/11111111-1111-4111-8111-111111111111\");\ncurl_setopt_array($request, [\n  CURLOPT_CUSTOMREQUEST => \"GET\",\n  CURLOPT_RETURNTRANSFER => true,\n  CURLOPT_TIMEOUT => 10,\n  CURLOPT_HTTPHEADER => [\"Authorization: Bearer \" . getenv(\"REVKEEN_PORTAL_TOKEN\"), \"Content-Type: application/json\"],\n]);\n$response = curl_exec($request);\nif ($response === false) throw new RuntimeException(curl_error($request));\n$status = curl_getinfo($request, CURLINFO_HTTP_CODE);\ncurl_close($request);\nif ($status >= 400) throw new RuntimeException(\"RevKeen request failed ($status)\");\necho $response;"},{"lang":"python","label":"Python","source":"import os\n\nimport requests\n\nresponse = requests.get(\n    \"https://api.revkeen.com/v2/customer-portal/invoices/11111111-1111-4111-8111-111111111111\",\n    headers={\"Authorization\": \"Bearer \" + os.environ[\"REVKEEN_PORTAL_TOKEN\"]},\n)\n\nresponse.raise_for_status()\nprint(response.json())"},{"lang":"javascript","label":"JavaScript","source":"const credential = process.env.REVKEEN_PORTAL_TOKEN;\nif (!credential) throw new Error(\"Set REVKEEN_PORTAL_TOKEN\");\nconst response = await fetch(\"https://api.revkeen.com/v2/customer-portal/invoices/11111111-1111-4111-8111-111111111111\", {\n  method: \"GET\",\n  headers: {\n    \"Authorization\": `Bearer ${credential}`,\n  },\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"java","label":"Java","source":"import java.net.URI;\nimport java.net.http.HttpClient;\nimport java.net.http.HttpRequest;\nimport java.net.http.HttpResponse;\n\nHttpClient client = HttpClient.newHttpClient();\n\nHttpRequest request = HttpRequest.newBuilder()\n    .uri(URI.create(\"https://api.revkeen.com/v2/customer-portal/invoices/11111111-1111-4111-8111-111111111111\"))\n    .header(\"Authorization\", \"Bearer \" + System.getenv(\"REVKEEN_PORTAL_TOKEN\"))\n    .GET()\n    .build();\n\nHttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());\nSystem.out.println(response.body());"},{"lang":"ruby","label":"Ruby","source":"require \"net/http\"\nrequire \"json\"\nrequire \"uri\"\n\nuri = URI(\"https://api.revkeen.com/v2/customer-portal/invoices/11111111-1111-4111-8111-111111111111\")\nhttp = Net::HTTP.new(uri.host, uri.port)\nhttp.use_ssl = true\n\nrequest = Net::HTTP::Get.new(uri)\nrequest[\"Authorization\"] = \"Bearer \" + ENV[\"REVKEEN_PORTAL_TOKEN\"]\n\nresponse = http.request(request)\nputs response.body"}]}},"/customer-portal/mandates":{"get":{"operationId":"customer_portal_mandates_list","tags":["Customer Portal"],"x-fern-sdk-group-name":["customerPortal","mandates"],"summary":"List the authenticated customer's Direct Debit mandates","description":"Returns the customer's Direct Debit mandates (status, masked account, next collection date). Requires the merchant to have portal DD self-service enabled.\n\n---\n\n**Related endpoints**\n\n- `POST /customer-portal/sessions` — Create a customer-portal session\n- `GET /customer-portal/customer` — Retrieve the authenticated customer\n- `GET /customer-portal/subscriptions` — List the authenticated customer's subscriptions\n- `GET /customer-portal/subscriptions/{id}` — Retrieve a subscription\n- `POST /customer-portal/subscriptions/{id}/cancel` — Cancel a subscription\n- `GET /customer-portal/invoices` — List the authenticated customer's invoices\n- `GET /customer-portal/invoices/{id}` — Retrieve an invoice\n- `POST /customer-portal/mandates/{id}/re-authorize` — Re-authorise a Direct Debit mandate with new bank details\n\n**Common errors**\n\n- `401 authentication_error` — missing, invalid, expired, or revoked credential. Codes: `authentication_failed`, `invalid_api_key`, `expired_api_key`, `api_key_revoked`, `session_invalid`, `merchant_required`. Carries a `WWW-Authenticate: Bearer` challenge.\n- `403 authorization_error` — the caller is identified but the action is denied. Codes include `insufficient_permissions`, `ip_not_allowed`, `merchant_mismatch`, and `origin_not_allowed`.\n\n**Pagination**\n\nOffset-based with `limit` (default 25, max 100) and `offset`. The response `pagination` block includes `total` and `hasMore`. See [the pagination guide](/docs/fundamentals/pagination) for SDK auto-paging helpers.","responses":{"200":{"description":"The customer's Direct Debit mandates.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PortalMandateList"}}}},"401":{"description":"Unauthorized - Invalid or missing API key","headers":{"WWW-Authenticate":{"schema":{"type":"string","description":"RFC 6750 Bearer authentication challenge","example":"Bearer realm=\"revkeen-api\""},"required":true,"description":"RFC 6750 Bearer authentication challenge"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthenticationError"}}}},"403":{"description":"Forbidden - Insufficient permissions","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ForbiddenError"}}}},"429":{"description":"Too Many Requests - Rate limit exceeded","headers":{"Retry-After":{"description":"Whole seconds until the caller may retry","schema":{"type":"string"},"example":"60"},"X-RateLimit-Limit":{"description":"Request quota for the current window","schema":{"type":"string"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window","schema":{"type":"string"}},"X-RateLimit-Reset":{"description":"UNIX timestamp in seconds when the window resets","schema":{"type":"string"}}}}},"x-cli-group":"customer-portal","x-cli-name":"mandates-list","x-codeSamples":[{"lang":"curl","label":"cURL","source":"curl -X GET \"https://api.revkeen.com/v2/customer-portal/mandates\" \\\n  -H \"Authorization: Bearer $REVKEEN_PORTAL_TOKEN\""},{"lang":"typescript","label":"TypeScript (HTTP)","source":"const credential = process.env.REVKEEN_PORTAL_TOKEN;\nif (!credential) throw new Error(\"Set REVKEEN_PORTAL_TOKEN\");\nconst response = await fetch(\"https://api.revkeen.com/v2/customer-portal/mandates\", {\n  method: \"GET\",\n  headers: {\n    \"Authorization\": `Bearer ${credential}`,\n  },\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"go","label":"Go (HTTP)","source":"package main\nimport (\"fmt\"; \"io\"; \"net/http\"; \"os\"; \"strings\"; \"time\")\nfunc main() {\n  req, err := http.NewRequest(\"GET\", \"https://api.revkeen.com/v2/customer-portal/mandates\", strings.NewReader(\"\"))\n  if err != nil { panic(err) }\n  req.Header.Set(\"Authorization\", \"Bearer \" + os.Getenv(\"REVKEEN_PORTAL_TOKEN\"))\n  req.Header.Set(\"Content-Type\", \"application/json\")\n  client := &http.Client{Timeout: 10 * time.Second}\n  response, err := client.Do(req)\n  if err != nil { panic(err) }\n  defer response.Body.Close()\n  if response.StatusCode >= 400 { panic(fmt.Sprintf(\"RevKeen request failed (%d)\", response.StatusCode)) }\n  data, err := io.ReadAll(response.Body)\n  if err != nil { panic(err) }\n  fmt.Println(string(data))\n}"},{"lang":"php","label":"PHP (HTTP)","source":"<?php\n$request = curl_init(\"https://api.revkeen.com/v2/customer-portal/mandates\");\ncurl_setopt_array($request, [\n  CURLOPT_CUSTOMREQUEST => \"GET\",\n  CURLOPT_RETURNTRANSFER => true,\n  CURLOPT_TIMEOUT => 10,\n  CURLOPT_HTTPHEADER => [\"Authorization: Bearer \" . getenv(\"REVKEEN_PORTAL_TOKEN\"), \"Content-Type: application/json\"],\n]);\n$response = curl_exec($request);\nif ($response === false) throw new RuntimeException(curl_error($request));\n$status = curl_getinfo($request, CURLINFO_HTTP_CODE);\ncurl_close($request);\nif ($status >= 400) throw new RuntimeException(\"RevKeen request failed ($status)\");\necho $response;"},{"lang":"python","label":"Python","source":"import os\n\nimport requests\n\nresponse = requests.get(\n    \"https://api.revkeen.com/v2/customer-portal/mandates\",\n    headers={\"Authorization\": \"Bearer \" + os.environ[\"REVKEEN_PORTAL_TOKEN\"]},\n)\n\nresponse.raise_for_status()\nprint(response.json())"},{"lang":"javascript","label":"JavaScript","source":"const credential = process.env.REVKEEN_PORTAL_TOKEN;\nif (!credential) throw new Error(\"Set REVKEEN_PORTAL_TOKEN\");\nconst response = await fetch(\"https://api.revkeen.com/v2/customer-portal/mandates\", {\n  method: \"GET\",\n  headers: {\n    \"Authorization\": `Bearer ${credential}`,\n  },\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"java","label":"Java","source":"import java.net.URI;\nimport java.net.http.HttpClient;\nimport java.net.http.HttpRequest;\nimport java.net.http.HttpResponse;\n\nHttpClient client = HttpClient.newHttpClient();\n\nHttpRequest request = HttpRequest.newBuilder()\n    .uri(URI.create(\"https://api.revkeen.com/v2/customer-portal/mandates\"))\n    .header(\"Authorization\", \"Bearer \" + System.getenv(\"REVKEEN_PORTAL_TOKEN\"))\n    .GET()\n    .build();\n\nHttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());\nSystem.out.println(response.body());"},{"lang":"ruby","label":"Ruby","source":"require \"net/http\"\nrequire \"json\"\nrequire \"uri\"\n\nuri = URI(\"https://api.revkeen.com/v2/customer-portal/mandates\")\nhttp = Net::HTTP.new(uri.host, uri.port)\nhttp.use_ssl = true\n\nrequest = Net::HTTP::Get.new(uri)\nrequest[\"Authorization\"] = \"Bearer \" + ENV[\"REVKEEN_PORTAL_TOKEN\"]\n\nresponse = http.request(request)\nputs response.body"}]}},"/customer-portal/mandates/{id}/re-authorize":{"post":{"operationId":"customer_portal_mandates_reauthorize","tags":["Customer Portal"],"x-fern-sdk-group-name":["customerPortal","mandates"],"summary":"Re-authorise a Direct Debit mandate with new bank details","description":"Updates the existing provider customer with new bank details and returns the mandate to pending while the fresh Bacs instruction is lodged. Requires portal DD self-service.\n\n---\n\n**Related endpoints**\n\n- `POST /customer-portal/sessions` — Create a customer-portal session\n- `GET /customer-portal/customer` — Retrieve the authenticated customer\n- `GET /customer-portal/subscriptions` — List the authenticated customer's subscriptions\n- `GET /customer-portal/subscriptions/{id}` — Retrieve a subscription\n- `POST /customer-portal/subscriptions/{id}/cancel` — Cancel a subscription\n- `GET /customer-portal/invoices` — List the authenticated customer's invoices\n- `GET /customer-portal/invoices/{id}` — Retrieve an invoice\n- `GET /customer-portal/mandates` — List the authenticated customer's Direct Debit mandates\n\n**Common errors**\n\n- `400 invalid_request` — malformed payload or failed validation.\n- `401 authentication_error` — missing, invalid, expired, or revoked credential. Codes: `authentication_failed`, `invalid_api_key`, `expired_api_key`, `api_key_revoked`, `session_invalid`, `merchant_required`. Carries a `WWW-Authenticate: Bearer` challenge.\n- `403 authorization_error` — the caller is identified but the action is denied. Codes include `insufficient_permissions`, `ip_not_allowed`, `merchant_mismatch`, and `origin_not_allowed`.\n- `404 resource_missing` — the referenced resource does not exist or is not visible to your key.\n- `409 conflict` — Idempotency-Key collision with a different body, or a concurrent state-transition conflict.\n- `422 unprocessable_entity` — business-rule failure (for example, refunding more than the original charge).\n\n**Idempotency**\n\nPass an `Idempotency-Key` header (UUID v4 recommended) to make retries safe. Keys are valid for 24 hours; see [the idempotency guide](/docs/fundamentals/idempotency).","parameters":[{"schema":{"type":"string","format":"uuid","description":"ID of the mandate being amended."},"required":true,"description":"ID of the mandate being amended.","name":"id","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PortalReauthorizeMandateRequest"}}}},"responses":{"201":{"description":"Existing mandate updated and returned to pending provider confirmation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PortalReauthorizeMandateResponse"}}}},"400":{"description":"Validation failed or DD not enabled.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CustomerPortalErrorResponse"}}}},"401":{"description":"Unauthorized - Invalid or missing API key","headers":{"WWW-Authenticate":{"schema":{"type":"string","description":"RFC 6750 Bearer authentication challenge","example":"Bearer realm=\"revkeen-api\""},"required":true,"description":"RFC 6750 Bearer authentication challenge"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthenticationError"}}}},"403":{"description":"Forbidden - Insufficient permissions","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ForbiddenError"}}}},"404":{"description":"Mandate not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CustomerPortalErrorResponse"}}}},"409":{"description":"A bank-detail amendment is already in progress or the mandate changed concurrently.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CustomerPortalErrorResponse"}}}},"422":{"description":"Customer record is missing required Direct Debit fields.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CustomerPortalErrorResponse"}}}},"429":{"description":"Too Many Requests - Rate limit exceeded","headers":{"Retry-After":{"description":"Whole seconds until the caller may retry","schema":{"type":"string"},"example":"60"},"X-RateLimit-Limit":{"description":"Request quota for the current window","schema":{"type":"string"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window","schema":{"type":"string"}},"X-RateLimit-Reset":{"description":"UNIX timestamp in seconds when the window resets","schema":{"type":"string"}}}},"500":{"description":"The provider accepted the amendment but local persistence requires reconciliation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CustomerPortalErrorResponse"}}}},"502":{"description":"The provider rejected the amendment or its outcome requires reconciliation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CustomerPortalErrorResponse"}}}},"503":{"description":"Secure bank-detail storage is unavailable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CustomerPortalErrorResponse"}}}}},"x-cli-group":"customer-portal","x-cli-name":"mandates-reauthorize","x-codeSamples":[{"lang":"curl","label":"cURL","source":"curl -X POST \"https://api.revkeen.com/v2/customer-portal/mandates/11111111-1111-4111-8111-111111111111/re-authorize\" \\\n  -H \"Authorization: Bearer $REVKEEN_PORTAL_TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"account_holder_name\": \"example\",\n    \"sort_code\": \"200000\",\n    \"account_number\": \"12345678\",\n    \"verification_token\": \"example\",\n    \"acceptance\": {\n      \"accountHolder\": true,\n      \"soleSignatory\": true\n    }\n  }'"},{"lang":"typescript","label":"TypeScript (HTTP)","source":"const credential = process.env.REVKEEN_PORTAL_TOKEN;\nif (!credential) throw new Error(\"Set REVKEEN_PORTAL_TOKEN\");\nconst response = await fetch(\"https://api.revkeen.com/v2/customer-portal/mandates/11111111-1111-4111-8111-111111111111/re-authorize\", {\n  method: \"POST\",\n  headers: {\n    \"Authorization\": `Bearer ${credential}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n    \"account_holder_name\": \"example\",\n    \"sort_code\": \"200000\",\n    \"account_number\": \"12345678\",\n    \"verification_token\": \"example\",\n    \"acceptance\": {\n      \"accountHolder\": true,\n      \"soleSignatory\": true\n    }\n  }),\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"go","label":"Go (HTTP)","source":"package main\nimport (\"fmt\"; \"io\"; \"net/http\"; \"os\"; \"strings\"; \"time\")\nfunc main() {\n  req, err := http.NewRequest(\"POST\", \"https://api.revkeen.com/v2/customer-portal/mandates/11111111-1111-4111-8111-111111111111/re-authorize\", strings.NewReader(\"{\\\"account_holder_name\\\":\\\"example\\\",\\\"sort_code\\\":\\\"200000\\\",\\\"account_number\\\":\\\"12345678\\\",\\\"verification_token\\\":\\\"example\\\",\\\"acceptance\\\":{\\\"accountHolder\\\":true,\\\"soleSignatory\\\":true}}\"))\n  if err != nil { panic(err) }\n  req.Header.Set(\"Authorization\", \"Bearer \" + os.Getenv(\"REVKEEN_PORTAL_TOKEN\"))\n  req.Header.Set(\"Content-Type\", \"application/json\")\n  client := &http.Client{Timeout: 10 * time.Second}\n  response, err := client.Do(req)\n  if err != nil { panic(err) }\n  defer response.Body.Close()\n  if response.StatusCode >= 400 { panic(fmt.Sprintf(\"RevKeen request failed (%d)\", response.StatusCode)) }\n  data, err := io.ReadAll(response.Body)\n  if err != nil { panic(err) }\n  fmt.Println(string(data))\n}"},{"lang":"php","label":"PHP (HTTP)","source":"<?php\n$request = curl_init(\"https://api.revkeen.com/v2/customer-portal/mandates/11111111-1111-4111-8111-111111111111/re-authorize\");\ncurl_setopt_array($request, [\n  CURLOPT_CUSTOMREQUEST => \"POST\",\n  CURLOPT_RETURNTRANSFER => true,\n  CURLOPT_TIMEOUT => 10,\n  CURLOPT_HTTPHEADER => [\"Authorization: Bearer \" . getenv(\"REVKEEN_PORTAL_TOKEN\"), \"Content-Type: application/json\"],\n  CURLOPT_POSTFIELDS => '{\\\"account_holder_name\\\":\\\"example\\\",\\\"sort_code\\\":\\\"200000\\\",\\\"account_number\\\":\\\"12345678\\\",\\\"verification_token\\\":\\\"example\\\",\\\"acceptance\\\":{\\\"accountHolder\\\":true,\\\"soleSignatory\\\":true}}',\n]);\n$response = curl_exec($request);\nif ($response === false) throw new RuntimeException(curl_error($request));\n$status = curl_getinfo($request, CURLINFO_HTTP_CODE);\ncurl_close($request);\nif ($status >= 400) throw new RuntimeException(\"RevKeen request failed ($status)\");\necho $response;"},{"lang":"python","label":"Python","source":"import os\n\nimport requests\n\nresponse = requests.post(\n    \"https://api.revkeen.com/v2/customer-portal/mandates/11111111-1111-4111-8111-111111111111/re-authorize\",\n    headers={\"Authorization\": \"Bearer \" + os.environ[\"REVKEEN_PORTAL_TOKEN\"]},\n    json={\n        \"account_holder_name\": \"example\",\n        \"sort_code\": \"200000\",\n        \"account_number\": \"12345678\",\n        \"verification_token\": \"example\",\n        \"acceptance\": {\n            \"accountHolder\": True,\n            \"soleSignatory\": True,\n        },\n    },\n)\n\nresponse.raise_for_status()\nprint(response.json())"},{"lang":"javascript","label":"JavaScript","source":"const credential = process.env.REVKEEN_PORTAL_TOKEN;\nif (!credential) throw new Error(\"Set REVKEEN_PORTAL_TOKEN\");\nconst response = await fetch(\"https://api.revkeen.com/v2/customer-portal/mandates/11111111-1111-4111-8111-111111111111/re-authorize\", {\n  method: \"POST\",\n  headers: {\n    \"Authorization\": `Bearer ${credential}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n    \"account_holder_name\": \"example\",\n    \"sort_code\": \"200000\",\n    \"account_number\": \"12345678\",\n    \"verification_token\": \"example\",\n    \"acceptance\": {\n      \"accountHolder\": true,\n      \"soleSignatory\": true\n    }\n  }),\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"java","label":"Java","source":"import java.net.URI;\nimport java.net.http.HttpClient;\nimport java.net.http.HttpRequest;\nimport java.net.http.HttpResponse;\n\nHttpClient client = HttpClient.newHttpClient();\n\nHttpRequest request = HttpRequest.newBuilder()\n    .uri(URI.create(\"https://api.revkeen.com/v2/customer-portal/mandates/11111111-1111-4111-8111-111111111111/re-authorize\"))\n    .header(\"Authorization\", \"Bearer \" + System.getenv(\"REVKEEN_PORTAL_TOKEN\"))\n    .header(\"Content-Type\", \"application/json\")\n    .method(\"POST\", HttpRequest.BodyPublishers.ofString(\"\"\"\n        {\n          \"account_holder_name\": \"example\",\n          \"sort_code\": \"200000\",\n          \"account_number\": \"12345678\",\n          \"verification_token\": \"example\",\n          \"acceptance\": {\n            \"accountHolder\": true,\n            \"soleSignatory\": true\n          }\n        }\n        \"\"\"))\n    .build();\n\nHttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());\nSystem.out.println(response.body());"},{"lang":"ruby","label":"Ruby","source":"require \"net/http\"\nrequire \"json\"\nrequire \"uri\"\n\nuri = URI(\"https://api.revkeen.com/v2/customer-portal/mandates/11111111-1111-4111-8111-111111111111/re-authorize\")\nhttp = Net::HTTP.new(uri.host, uri.port)\nhttp.use_ssl = true\n\nrequest = Net::HTTP::Post.new(uri)\nrequest[\"Authorization\"] = \"Bearer \" + ENV[\"REVKEEN_PORTAL_TOKEN\"]\nrequest[\"Content-Type\"] = \"application/json\"\nrequest.body = {\n  \"account_holder_name\" => \"example\",\n  \"sort_code\" => \"200000\",\n  \"account_number\" => \"12345678\",\n  \"verification_token\" => \"example\",\n  \"acceptance\" => {\n    \"accountHolder\" => true,\n    \"soleSignatory\" => true,\n  },\n}.to_json\n\nresponse = http.request(request)\nputs response.body"}]}},"/customer-portal/mandates/{id}/cancel":{"post":{"operationId":"customer_portal_mandates_cancel","tags":["Customer Portal"],"x-fern-sdk-group-name":["customerPortal","mandates"],"summary":"Cancel a Direct Debit mandate","description":"Cancels the mandate for future collections only. Requires portal DD self-service. Bank changes affect future only (per approved V13 mockup).\n\n---\n\n**Related endpoints**\n\n- `POST /customer-portal/sessions` — Create a customer-portal session\n- `GET /customer-portal/customer` — Retrieve the authenticated customer\n- `GET /customer-portal/subscriptions` — List the authenticated customer's subscriptions\n- `GET /customer-portal/subscriptions/{id}` — Retrieve a subscription\n- `POST /customer-portal/subscriptions/{id}/cancel` — Cancel a subscription\n- `GET /customer-portal/invoices` — List the authenticated customer's invoices\n- `GET /customer-portal/invoices/{id}` — Retrieve an invoice\n- `GET /customer-portal/mandates` — List the authenticated customer's Direct Debit mandates\n\n**Common errors**\n\n- `400 invalid_request` — malformed payload or failed validation.\n- `403 authorization_error` — the caller is identified but the action is denied. Codes include `insufficient_permissions`, `ip_not_allowed`, `merchant_mismatch`, and `origin_not_allowed`.\n- `404 resource_missing` — the referenced resource does not exist or is not visible to your key.\n\n**Idempotency**\n\nPass an `Idempotency-Key` header (UUID v4 recommended) to make retries safe. Keys are valid for 24 hours; see [the idempotency guide](/docs/fundamentals/idempotency).","parameters":[{"schema":{"type":"string","format":"uuid","description":"Mandate ID"},"required":true,"description":"Mandate ID","name":"id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"reason":{"type":"string"}}}}}},"responses":{"200":{"description":"Mandate cancelled.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/PortalMandate"}},"required":["data"]}}}},"400":{"description":"Already cancelled or invalid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CustomerPortalErrorResponse"}}}},"401":{"description":"Unauthorized - Invalid or missing API key","headers":{"WWW-Authenticate":{"description":"RFC 6750 Bearer authentication challenge","schema":{"type":"string"},"example":"Bearer realm=\"revkeen-api\""}}},"403":{"description":"Forbidden - Insufficient permissions","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ForbiddenError"}}}},"404":{"description":"Not found or not owned.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CustomerPortalErrorResponse"}}}},"429":{"description":"Too Many Requests - Rate limit exceeded","headers":{"Retry-After":{"description":"Whole seconds until the caller may retry","schema":{"type":"string"},"example":"60"},"X-RateLimit-Limit":{"description":"Request quota for the current window","schema":{"type":"string"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window","schema":{"type":"string"}},"X-RateLimit-Reset":{"description":"UNIX timestamp in seconds when the window resets","schema":{"type":"string"}}}}},"x-cli-group":"customer-portal","x-cli-name":"mandates-cancel","x-codeSamples":[{"lang":"curl","label":"cURL","source":"curl -X POST \"https://api.revkeen.com/v2/customer-portal/mandates/11111111-1111-4111-8111-111111111111/cancel\" \\\n  -H \"Authorization: Bearer $REVKEEN_PORTAL_TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"reason\": \"example\"\n  }'"},{"lang":"typescript","label":"TypeScript (HTTP)","source":"const credential = process.env.REVKEEN_PORTAL_TOKEN;\nif (!credential) throw new Error(\"Set REVKEEN_PORTAL_TOKEN\");\nconst response = await fetch(\"https://api.revkeen.com/v2/customer-portal/mandates/11111111-1111-4111-8111-111111111111/cancel\", {\n  method: \"POST\",\n  headers: {\n    \"Authorization\": `Bearer ${credential}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n    \"reason\": \"example\"\n  }),\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"go","label":"Go (HTTP)","source":"package main\nimport (\"fmt\"; \"io\"; \"net/http\"; \"os\"; \"strings\"; \"time\")\nfunc main() {\n  req, err := http.NewRequest(\"POST\", \"https://api.revkeen.com/v2/customer-portal/mandates/11111111-1111-4111-8111-111111111111/cancel\", strings.NewReader(\"{\\\"reason\\\":\\\"example\\\"}\"))\n  if err != nil { panic(err) }\n  req.Header.Set(\"Authorization\", \"Bearer \" + os.Getenv(\"REVKEEN_PORTAL_TOKEN\"))\n  req.Header.Set(\"Content-Type\", \"application/json\")\n  client := &http.Client{Timeout: 10 * time.Second}\n  response, err := client.Do(req)\n  if err != nil { panic(err) }\n  defer response.Body.Close()\n  if response.StatusCode >= 400 { panic(fmt.Sprintf(\"RevKeen request failed (%d)\", response.StatusCode)) }\n  data, err := io.ReadAll(response.Body)\n  if err != nil { panic(err) }\n  fmt.Println(string(data))\n}"},{"lang":"php","label":"PHP (HTTP)","source":"<?php\n$request = curl_init(\"https://api.revkeen.com/v2/customer-portal/mandates/11111111-1111-4111-8111-111111111111/cancel\");\ncurl_setopt_array($request, [\n  CURLOPT_CUSTOMREQUEST => \"POST\",\n  CURLOPT_RETURNTRANSFER => true,\n  CURLOPT_TIMEOUT => 10,\n  CURLOPT_HTTPHEADER => [\"Authorization: Bearer \" . getenv(\"REVKEEN_PORTAL_TOKEN\"), \"Content-Type: application/json\"],\n  CURLOPT_POSTFIELDS => '{\\\"reason\\\":\\\"example\\\"}',\n]);\n$response = curl_exec($request);\nif ($response === false) throw new RuntimeException(curl_error($request));\n$status = curl_getinfo($request, CURLINFO_HTTP_CODE);\ncurl_close($request);\nif ($status >= 400) throw new RuntimeException(\"RevKeen request failed ($status)\");\necho $response;"},{"lang":"python","label":"Python","source":"import os\n\nimport requests\n\nresponse = requests.post(\n    \"https://api.revkeen.com/v2/customer-portal/mandates/11111111-1111-4111-8111-111111111111/cancel\",\n    headers={\"Authorization\": \"Bearer \" + os.environ[\"REVKEEN_PORTAL_TOKEN\"]},\n    json={\n        \"reason\": \"example\",\n    },\n)\n\nresponse.raise_for_status()\nprint(response.json())"},{"lang":"javascript","label":"JavaScript","source":"const credential = process.env.REVKEEN_PORTAL_TOKEN;\nif (!credential) throw new Error(\"Set REVKEEN_PORTAL_TOKEN\");\nconst response = await fetch(\"https://api.revkeen.com/v2/customer-portal/mandates/11111111-1111-4111-8111-111111111111/cancel\", {\n  method: \"POST\",\n  headers: {\n    \"Authorization\": `Bearer ${credential}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n    \"reason\": \"example\"\n  }),\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"java","label":"Java","source":"import java.net.URI;\nimport java.net.http.HttpClient;\nimport java.net.http.HttpRequest;\nimport java.net.http.HttpResponse;\n\nHttpClient client = HttpClient.newHttpClient();\n\nHttpRequest request = HttpRequest.newBuilder()\n    .uri(URI.create(\"https://api.revkeen.com/v2/customer-portal/mandates/11111111-1111-4111-8111-111111111111/cancel\"))\n    .header(\"Authorization\", \"Bearer \" + System.getenv(\"REVKEEN_PORTAL_TOKEN\"))\n    .header(\"Content-Type\", \"application/json\")\n    .method(\"POST\", HttpRequest.BodyPublishers.ofString(\"\"\"\n        {\n          \"reason\": \"example\"\n        }\n        \"\"\"))\n    .build();\n\nHttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());\nSystem.out.println(response.body());"},{"lang":"ruby","label":"Ruby","source":"require \"net/http\"\nrequire \"json\"\nrequire \"uri\"\n\nuri = URI(\"https://api.revkeen.com/v2/customer-portal/mandates/11111111-1111-4111-8111-111111111111/cancel\")\nhttp = Net::HTTP.new(uri.host, uri.port)\nhttp.use_ssl = true\n\nrequest = Net::HTTP::Post.new(uri)\nrequest[\"Authorization\"] = \"Bearer \" + ENV[\"REVKEEN_PORTAL_TOKEN\"]\nrequest[\"Content-Type\"] = \"application/json\"\nrequest.body = {\n  \"reason\" => \"example\",\n}.to_json\n\nresponse = http.request(request)\nputs response.body"}]}},"/customer-portal/mandates/{id}/re-authorize/send-otp":{"post":{"operationId":"customer_portal_mandates_reauthorize_send_otp","tags":["Customer Portal"],"x-fern-sdk-group-name":["customerPortal","mandates"],"summary":"Send a step-up OTP for a Direct Debit bank-detail change","description":"Emails the customer a one-time code that must be verified (verify-otp) before a change-of-bank can be submitted.\n\n---\n\n**Related endpoints**\n\n- `POST /customer-portal/sessions` — Create a customer-portal session\n- `GET /customer-portal/customer` — Retrieve the authenticated customer\n- `GET /customer-portal/subscriptions` — List the authenticated customer's subscriptions\n- `GET /customer-portal/subscriptions/{id}` — Retrieve a subscription\n- `POST /customer-portal/subscriptions/{id}/cancel` — Cancel a subscription\n- `GET /customer-portal/invoices` — List the authenticated customer's invoices\n- `GET /customer-portal/invoices/{id}` — Retrieve an invoice\n- `GET /customer-portal/mandates` — List the authenticated customer's Direct Debit mandates\n\n**Common errors**\n\n- `400 invalid_request` — malformed payload or failed validation.\n- `403 authorization_error` — the caller is identified but the action is denied. Codes include `insufficient_permissions`, `ip_not_allowed`, `merchant_mismatch`, and `origin_not_allowed`.\n- `404 resource_missing` — the referenced resource does not exist or is not visible to your key.\n- `409 conflict` — Idempotency-Key collision with a different body, or a concurrent state-transition conflict.\n- `422 unprocessable_entity` — business-rule failure (for example, refunding more than the original charge).\n\n**Idempotency**\n\nPass an `Idempotency-Key` header (UUID v4 recommended) to make retries safe. Keys are valid for 24 hours; see [the idempotency guide](/docs/fundamentals/idempotency).","parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"}],"responses":{"200":{"description":"OTP dispatched (or silently rate-limited).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PortalReauthorizeSendOtpResponse"}}}},"400":{"description":"DD not enabled.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CustomerPortalErrorResponse"}}}},"401":{"description":"Unauthorized - Invalid or missing API key","headers":{"WWW-Authenticate":{"description":"RFC 6750 Bearer authentication challenge","schema":{"type":"string"},"example":"Bearer realm=\"revkeen-api\""}}},"403":{"description":"Forbidden - Insufficient permissions","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ForbiddenError"}}}},"404":{"description":"Mandate not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CustomerPortalErrorResponse"}}}},"409":{"description":"Mandate not in an amendable state.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CustomerPortalErrorResponse"}}}},"422":{"description":"Customer record is missing an email.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CustomerPortalErrorResponse"}}}},"429":{"description":"Too Many Requests - Rate limit exceeded","headers":{"Retry-After":{"description":"Whole seconds until the caller may retry","schema":{"type":"string"},"example":"60"},"X-RateLimit-Limit":{"description":"Request quota for the current window","schema":{"type":"string"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window","schema":{"type":"string"}},"X-RateLimit-Reset":{"description":"UNIX timestamp in seconds when the window resets","schema":{"type":"string"}}}}},"x-cli-group":"customer-portal","x-cli-name":"mandates-reauthorize-send-otp","x-codeSamples":[{"lang":"curl","label":"cURL","source":"curl -X POST \"https://api.revkeen.com/v2/customer-portal/mandates/11111111-1111-4111-8111-111111111111/re-authorize/send-otp\" \\\n  -H \"Authorization: Bearer $REVKEEN_PORTAL_TOKEN\""},{"lang":"typescript","label":"TypeScript (HTTP)","source":"const credential = process.env.REVKEEN_PORTAL_TOKEN;\nif (!credential) throw new Error(\"Set REVKEEN_PORTAL_TOKEN\");\nconst response = await fetch(\"https://api.revkeen.com/v2/customer-portal/mandates/11111111-1111-4111-8111-111111111111/re-authorize/send-otp\", {\n  method: \"POST\",\n  headers: {\n    \"Authorization\": `Bearer ${credential}`,\n  },\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"go","label":"Go (HTTP)","source":"package main\nimport (\"fmt\"; \"io\"; \"net/http\"; \"os\"; \"strings\"; \"time\")\nfunc main() {\n  req, err := http.NewRequest(\"POST\", \"https://api.revkeen.com/v2/customer-portal/mandates/11111111-1111-4111-8111-111111111111/re-authorize/send-otp\", strings.NewReader(\"\"))\n  if err != nil { panic(err) }\n  req.Header.Set(\"Authorization\", \"Bearer \" + os.Getenv(\"REVKEEN_PORTAL_TOKEN\"))\n  req.Header.Set(\"Content-Type\", \"application/json\")\n  client := &http.Client{Timeout: 10 * time.Second}\n  response, err := client.Do(req)\n  if err != nil { panic(err) }\n  defer response.Body.Close()\n  if response.StatusCode >= 400 { panic(fmt.Sprintf(\"RevKeen request failed (%d)\", response.StatusCode)) }\n  data, err := io.ReadAll(response.Body)\n  if err != nil { panic(err) }\n  fmt.Println(string(data))\n}"},{"lang":"php","label":"PHP (HTTP)","source":"<?php\n$request = curl_init(\"https://api.revkeen.com/v2/customer-portal/mandates/11111111-1111-4111-8111-111111111111/re-authorize/send-otp\");\ncurl_setopt_array($request, [\n  CURLOPT_CUSTOMREQUEST => \"POST\",\n  CURLOPT_RETURNTRANSFER => true,\n  CURLOPT_TIMEOUT => 10,\n  CURLOPT_HTTPHEADER => [\"Authorization: Bearer \" . getenv(\"REVKEEN_PORTAL_TOKEN\"), \"Content-Type: application/json\"],\n]);\n$response = curl_exec($request);\nif ($response === false) throw new RuntimeException(curl_error($request));\n$status = curl_getinfo($request, CURLINFO_HTTP_CODE);\ncurl_close($request);\nif ($status >= 400) throw new RuntimeException(\"RevKeen request failed ($status)\");\necho $response;"},{"lang":"python","label":"Python","source":"import os\n\nimport requests\n\nresponse = requests.post(\n    \"https://api.revkeen.com/v2/customer-portal/mandates/11111111-1111-4111-8111-111111111111/re-authorize/send-otp\",\n    headers={\"Authorization\": \"Bearer \" + os.environ[\"REVKEEN_PORTAL_TOKEN\"]},\n)\n\nresponse.raise_for_status()\nprint(response.json())"},{"lang":"javascript","label":"JavaScript","source":"const credential = process.env.REVKEEN_PORTAL_TOKEN;\nif (!credential) throw new Error(\"Set REVKEEN_PORTAL_TOKEN\");\nconst response = await fetch(\"https://api.revkeen.com/v2/customer-portal/mandates/11111111-1111-4111-8111-111111111111/re-authorize/send-otp\", {\n  method: \"POST\",\n  headers: {\n    \"Authorization\": `Bearer ${credential}`,\n  },\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"java","label":"Java","source":"import java.net.URI;\nimport java.net.http.HttpClient;\nimport java.net.http.HttpRequest;\nimport java.net.http.HttpResponse;\n\nHttpClient client = HttpClient.newHttpClient();\n\nHttpRequest request = HttpRequest.newBuilder()\n    .uri(URI.create(\"https://api.revkeen.com/v2/customer-portal/mandates/11111111-1111-4111-8111-111111111111/re-authorize/send-otp\"))\n    .header(\"Authorization\", \"Bearer \" + System.getenv(\"REVKEEN_PORTAL_TOKEN\"))\n    .method(\"POST\", HttpRequest.BodyPublishers.noBody())\n    .build();\n\nHttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());\nSystem.out.println(response.body());"},{"lang":"ruby","label":"Ruby","source":"require \"net/http\"\nrequire \"json\"\nrequire \"uri\"\n\nuri = URI(\"https://api.revkeen.com/v2/customer-portal/mandates/11111111-1111-4111-8111-111111111111/re-authorize/send-otp\")\nhttp = Net::HTTP.new(uri.host, uri.port)\nhttp.use_ssl = true\n\nrequest = Net::HTTP::Post.new(uri)\nrequest[\"Authorization\"] = \"Bearer \" + ENV[\"REVKEEN_PORTAL_TOKEN\"]\n\nresponse = http.request(request)\nputs response.body"}]}},"/customer-portal/mandates/{id}/re-authorize/verify-otp":{"post":{"operationId":"customer_portal_mandates_reauthorize_verify_otp","tags":["Customer Portal"],"x-fern-sdk-group-name":["customerPortal","mandates"],"summary":"Verify a step-up OTP and receive a change-of-bank token","description":"Verifies the one-time code and returns a short-lived verification_token bound to this customer, the change-bank action, and this mandate. Pass it to re-authorize.\n\n---\n\n**Related endpoints**\n\n- `POST /customer-portal/sessions` — Create a customer-portal session\n- `GET /customer-portal/customer` — Retrieve the authenticated customer\n- `GET /customer-portal/subscriptions` — List the authenticated customer's subscriptions\n- `GET /customer-portal/subscriptions/{id}` — Retrieve a subscription\n- `POST /customer-portal/subscriptions/{id}/cancel` — Cancel a subscription\n- `GET /customer-portal/invoices` — List the authenticated customer's invoices\n- `GET /customer-portal/invoices/{id}` — Retrieve an invoice\n- `GET /customer-portal/mandates` — List the authenticated customer's Direct Debit mandates\n\n**Common errors**\n\n- `400 invalid_request` — malformed payload or failed validation.\n- `403 authorization_error` — the caller is identified but the action is denied. Codes include `insufficient_permissions`, `ip_not_allowed`, `merchant_mismatch`, and `origin_not_allowed`.\n- `404 resource_missing` — the referenced resource does not exist or is not visible to your key.\n- `422 unprocessable_entity` — business-rule failure (for example, refunding more than the original charge).\n\n**Idempotency**\n\nPass an `Idempotency-Key` header (UUID v4 recommended) to make retries safe. Keys are valid for 24 hours; see [the idempotency guide](/docs/fundamentals/idempotency).","parameters":[{"schema":{"type":"string","format":"uuid"},"required":true,"name":"id","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PortalReauthorizeVerifyOtpRequest"}}}},"responses":{"200":{"description":"Code verified; token issued.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PortalReauthorizeVerifyOtpResponse"}}}},"400":{"description":"Invalid or expired code / DD not enabled.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CustomerPortalErrorResponse"}}}},"401":{"description":"Unauthorized - Invalid or missing API key","headers":{"WWW-Authenticate":{"description":"RFC 6750 Bearer authentication challenge","schema":{"type":"string"},"example":"Bearer realm=\"revkeen-api\""}}},"403":{"description":"Forbidden - Insufficient permissions","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ForbiddenError"}}}},"404":{"description":"Mandate not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CustomerPortalErrorResponse"}}}},"422":{"description":"Customer record is missing an email.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CustomerPortalErrorResponse"}}}},"429":{"description":"Too Many Requests - Rate limit exceeded","headers":{"Retry-After":{"description":"Whole seconds until the caller may retry","schema":{"type":"string"},"example":"60"},"X-RateLimit-Limit":{"description":"Request quota for the current window","schema":{"type":"string"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window","schema":{"type":"string"}},"X-RateLimit-Reset":{"description":"UNIX timestamp in seconds when the window resets","schema":{"type":"string"}}}}},"x-cli-group":"customer-portal","x-cli-name":"mandates-reauthorize-verify-otp","x-codeSamples":[{"lang":"curl","label":"cURL","source":"curl -X POST \"https://api.revkeen.com/v2/customer-portal/mandates/11111111-1111-4111-8111-111111111111/re-authorize/verify-otp\" \\\n  -H \"Authorization: Bearer $REVKEEN_PORTAL_TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"code\": \"example\"\n  }'"},{"lang":"typescript","label":"TypeScript (HTTP)","source":"const credential = process.env.REVKEEN_PORTAL_TOKEN;\nif (!credential) throw new Error(\"Set REVKEEN_PORTAL_TOKEN\");\nconst response = await fetch(\"https://api.revkeen.com/v2/customer-portal/mandates/11111111-1111-4111-8111-111111111111/re-authorize/verify-otp\", {\n  method: \"POST\",\n  headers: {\n    \"Authorization\": `Bearer ${credential}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n    \"code\": \"example\"\n  }),\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"go","label":"Go (HTTP)","source":"package main\nimport (\"fmt\"; \"io\"; \"net/http\"; \"os\"; \"strings\"; \"time\")\nfunc main() {\n  req, err := http.NewRequest(\"POST\", \"https://api.revkeen.com/v2/customer-portal/mandates/11111111-1111-4111-8111-111111111111/re-authorize/verify-otp\", strings.NewReader(\"{\\\"code\\\":\\\"example\\\"}\"))\n  if err != nil { panic(err) }\n  req.Header.Set(\"Authorization\", \"Bearer \" + os.Getenv(\"REVKEEN_PORTAL_TOKEN\"))\n  req.Header.Set(\"Content-Type\", \"application/json\")\n  client := &http.Client{Timeout: 10 * time.Second}\n  response, err := client.Do(req)\n  if err != nil { panic(err) }\n  defer response.Body.Close()\n  if response.StatusCode >= 400 { panic(fmt.Sprintf(\"RevKeen request failed (%d)\", response.StatusCode)) }\n  data, err := io.ReadAll(response.Body)\n  if err != nil { panic(err) }\n  fmt.Println(string(data))\n}"},{"lang":"php","label":"PHP (HTTP)","source":"<?php\n$request = curl_init(\"https://api.revkeen.com/v2/customer-portal/mandates/11111111-1111-4111-8111-111111111111/re-authorize/verify-otp\");\ncurl_setopt_array($request, [\n  CURLOPT_CUSTOMREQUEST => \"POST\",\n  CURLOPT_RETURNTRANSFER => true,\n  CURLOPT_TIMEOUT => 10,\n  CURLOPT_HTTPHEADER => [\"Authorization: Bearer \" . getenv(\"REVKEEN_PORTAL_TOKEN\"), \"Content-Type: application/json\"],\n  CURLOPT_POSTFIELDS => '{\\\"code\\\":\\\"example\\\"}',\n]);\n$response = curl_exec($request);\nif ($response === false) throw new RuntimeException(curl_error($request));\n$status = curl_getinfo($request, CURLINFO_HTTP_CODE);\ncurl_close($request);\nif ($status >= 400) throw new RuntimeException(\"RevKeen request failed ($status)\");\necho $response;"},{"lang":"python","label":"Python","source":"import os\n\nimport requests\n\nresponse = requests.post(\n    \"https://api.revkeen.com/v2/customer-portal/mandates/11111111-1111-4111-8111-111111111111/re-authorize/verify-otp\",\n    headers={\"Authorization\": \"Bearer \" + os.environ[\"REVKEEN_PORTAL_TOKEN\"]},\n    json={\n        \"code\": \"example\",\n    },\n)\n\nresponse.raise_for_status()\nprint(response.json())"},{"lang":"javascript","label":"JavaScript","source":"const credential = process.env.REVKEEN_PORTAL_TOKEN;\nif (!credential) throw new Error(\"Set REVKEEN_PORTAL_TOKEN\");\nconst response = await fetch(\"https://api.revkeen.com/v2/customer-portal/mandates/11111111-1111-4111-8111-111111111111/re-authorize/verify-otp\", {\n  method: \"POST\",\n  headers: {\n    \"Authorization\": `Bearer ${credential}`,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n    \"code\": \"example\"\n  }),\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"java","label":"Java","source":"import java.net.URI;\nimport java.net.http.HttpClient;\nimport java.net.http.HttpRequest;\nimport java.net.http.HttpResponse;\n\nHttpClient client = HttpClient.newHttpClient();\n\nHttpRequest request = HttpRequest.newBuilder()\n    .uri(URI.create(\"https://api.revkeen.com/v2/customer-portal/mandates/11111111-1111-4111-8111-111111111111/re-authorize/verify-otp\"))\n    .header(\"Authorization\", \"Bearer \" + System.getenv(\"REVKEEN_PORTAL_TOKEN\"))\n    .header(\"Content-Type\", \"application/json\")\n    .method(\"POST\", HttpRequest.BodyPublishers.ofString(\"\"\"\n        {\n          \"code\": \"example\"\n        }\n        \"\"\"))\n    .build();\n\nHttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());\nSystem.out.println(response.body());"},{"lang":"ruby","label":"Ruby","source":"require \"net/http\"\nrequire \"json\"\nrequire \"uri\"\n\nuri = URI(\"https://api.revkeen.com/v2/customer-portal/mandates/11111111-1111-4111-8111-111111111111/re-authorize/verify-otp\")\nhttp = Net::HTTP.new(uri.host, uri.port)\nhttp.use_ssl = true\n\nrequest = Net::HTTP::Post.new(uri)\nrequest[\"Authorization\"] = \"Bearer \" + ENV[\"REVKEEN_PORTAL_TOKEN\"]\nrequest[\"Content-Type\"] = \"application/json\"\nrequest.body = {\n  \"code\" => \"example\",\n}.to_json\n\nresponse = http.request(request)\nputs response.body"}]}},"/customer-meters":{"get":{"operationId":"customer_meters_list","tags":["Customer Meters"],"x-fern-sdk-group-name":["customerMeters"],"summary":"List a customer's meter usage","description":"Returns per-meter aggregates for a single customer. Requires a `customer_id` query parameter. For each meter the merchant has defined, the response includes the aggregate quantity (applying the meter's native aggregation function), the event count, and the most recent event timestamp.\n\nUse this to render a customer's current consumption snapshot in your own UI — for example, a usage dashboard showing \"4,820 / 10,000 requests this period\".\n\n---\n\n**Related endpoints**\n\n- `GET /customer-meters/{customer_id}/{meter_id}` — Retrieve a customer-meter aggregate\n\n**Common errors**\n\n- `400 invalid_request` — malformed payload or failed validation.\n- `401 authentication_error` — missing, invalid, expired, or revoked credential. Codes: `authentication_failed`, `invalid_api_key`, `expired_api_key`, `api_key_revoked`, `session_invalid`, `merchant_required`. Carries a `WWW-Authenticate: Bearer` challenge.\n\n**Pagination**\n\nOffset-based with `limit` (default 25, max 100) and `offset`. The response `pagination` block includes `total` and `hasMore`. See [the pagination guide](/docs/fundamentals/pagination) for SDK auto-paging helpers.","parameters":[{"schema":{"type":"string","format":"uuid","description":"Customer UUID. Required — customer-meters are not queryable merchant-wide via this endpoint. Use /v2/meters for merchant-level meter definitions."},"required":true,"description":"Customer UUID. Required — customer-meters are not queryable merchant-wide via this endpoint. Use /v2/meters for merchant-level meter definitions.","name":"customer_id","in":"query"},{"schema":{"type":"string","format":"uuid","description":"Optional single-meter filter. When provided, the response contains at most one entry."},"required":false,"description":"Optional single-meter filter. When provided, the response contains at most one entry.","name":"meter_id","in":"query"}],"responses":{"200":{"description":"List of customer-meter aggregates (one per meter the merchant has defined).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CustomerMeterList"}}}},"400":{"description":"Missing or invalid customer_id.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CustomerMeterErrorResponse"}}}},"401":{"description":"Unauthorized.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CustomerMeterErrorResponse"}}},"headers":{"WWW-Authenticate":{"description":"RFC 6750 Bearer authentication challenge","schema":{"type":"string"},"example":"Bearer realm=\"revkeen-api\""}}},"429":{"description":"Too Many Requests - Rate limit exceeded","headers":{"Retry-After":{"description":"Whole seconds until the caller may retry","schema":{"type":"string"},"example":"60"},"X-RateLimit-Limit":{"description":"Request quota for the current window","schema":{"type":"string"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window","schema":{"type":"string"}},"X-RateLimit-Reset":{"description":"UNIX timestamp in seconds when the window resets","schema":{"type":"string"}}}}},"x-cli-group":"customer-meters","x-cli-name":"list","x-codeSamples":[{"lang":"curl","label":"cURL","source":"curl -X GET \"https://api.revkeen.com/v2/customer-meters?customer_id=11111111-1111-4111-8111-111111111111&meter_id=11111111-1111-4111-8111-111111111111\" \\\n  -H \"x-api-key: $REVKEEN_API_KEY\""},{"lang":"typescript","label":"TypeScript (SDK)","source":"// npm install @revkeen/sdk@1.20260822.1541\nimport { RevKeenClient } from \"@revkeen/sdk\";\nconst apiKey = process.env.REVKEEN_API_KEY;\nif (!apiKey) throw new Error(\"Set REVKEEN_API_KEY\");\nconst client = new RevKeenClient({ apiKey, baseUrl: \"https://api.revkeen.com/v2\" });\nconst result = await client.customer.metersList({\n  \"customer_id\": \"11111111-1111-4111-8111-111111111111\",\n  \"meter_id\": \"11111111-1111-4111-8111-111111111111\"\n});\nconsole.log(result);"},{"lang":"go","label":"Go (HTTP)","source":"package main\nimport (\"fmt\"; \"io\"; \"net/http\"; \"os\"; \"strings\"; \"time\")\nfunc main() {\n  req, err := http.NewRequest(\"GET\", \"https://api.revkeen.com/v2/customer-meters?customer_id=11111111-1111-4111-8111-111111111111&meter_id=11111111-1111-4111-8111-111111111111\", strings.NewReader(\"\"))\n  if err != nil { panic(err) }\n  req.Header.Set(\"x-api-key\", os.Getenv(\"REVKEEN_API_KEY\"))\n  req.Header.Set(\"Content-Type\", \"application/json\")\n  client := &http.Client{Timeout: 10 * time.Second}\n  response, err := client.Do(req)\n  if err != nil { panic(err) }\n  defer response.Body.Close()\n  if response.StatusCode >= 400 { panic(fmt.Sprintf(\"RevKeen request failed (%d)\", response.StatusCode)) }\n  data, err := io.ReadAll(response.Body)\n  if err != nil { panic(err) }\n  fmt.Println(string(data))\n}"},{"lang":"php","label":"PHP (HTTP)","source":"<?php\n$request = curl_init(\"https://api.revkeen.com/v2/customer-meters?customer_id=11111111-1111-4111-8111-111111111111&meter_id=11111111-1111-4111-8111-111111111111\");\ncurl_setopt_array($request, [\n  CURLOPT_CUSTOMREQUEST => \"GET\",\n  CURLOPT_RETURNTRANSFER => true,\n  CURLOPT_TIMEOUT => 10,\n  CURLOPT_HTTPHEADER => [\"x-api-key: \" . getenv(\"REVKEEN_API_KEY\"), \"Content-Type: application/json\"],\n]);\n$response = curl_exec($request);\nif ($response === false) throw new RuntimeException(curl_error($request));\n$status = curl_getinfo($request, CURLINFO_HTTP_CODE);\ncurl_close($request);\nif ($status >= 400) throw new RuntimeException(\"RevKeen request failed ($status)\");\necho $response;"},{"lang":"python","label":"Python","source":"import os\n\nimport requests\n\nresponse = requests.get(\n    \"https://api.revkeen.com/v2/customer-meters\",\n    headers={\"x-api-key\": os.environ[\"REVKEEN_API_KEY\"]},\n    params={\n        \"customer_id\": \"11111111-1111-4111-8111-111111111111\",\n        \"meter_id\": \"11111111-1111-4111-8111-111111111111\",\n    },\n)\n\nresponse.raise_for_status()\nprint(response.json())"},{"lang":"javascript","label":"JavaScript","source":"const credential = process.env.REVKEEN_API_KEY;\nif (!credential) throw new Error(\"Set REVKEEN_API_KEY\");\nconst response = await fetch(\"https://api.revkeen.com/v2/customer-meters?customer_id=11111111-1111-4111-8111-111111111111&meter_id=11111111-1111-4111-8111-111111111111\", {\n  method: \"GET\",\n  headers: {\n    \"x-api-key\": credential,\n  },\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"java","label":"Java","source":"import java.net.URI;\nimport java.net.http.HttpClient;\nimport java.net.http.HttpRequest;\nimport java.net.http.HttpResponse;\n\nHttpClient client = HttpClient.newHttpClient();\n\nHttpRequest request = HttpRequest.newBuilder()\n    .uri(URI.create(\"https://api.revkeen.com/v2/customer-meters?customer_id=11111111-1111-4111-8111-111111111111&meter_id=11111111-1111-4111-8111-111111111111\"))\n    .header(\"x-api-key\", System.getenv(\"REVKEEN_API_KEY\"))\n    .GET()\n    .build();\n\nHttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());\nSystem.out.println(response.body());"},{"lang":"ruby","label":"Ruby","source":"require \"net/http\"\nrequire \"json\"\nrequire \"uri\"\n\nuri = URI(\"https://api.revkeen.com/v2/customer-meters?customer_id=11111111-1111-4111-8111-111111111111&meter_id=11111111-1111-4111-8111-111111111111\")\nhttp = Net::HTTP.new(uri.host, uri.port)\nhttp.use_ssl = true\n\nrequest = Net::HTTP::Get.new(uri)\nrequest[\"x-api-key\"] = ENV[\"REVKEEN_API_KEY\"]\n\nresponse = http.request(request)\nputs response.body"}],"security":[{"apiKey":["meters:read"]},{"oauth2":["meters:read"]}]}},"/customer-meters/{customer_id}/{meter_id}":{"get":{"operationId":"customer_meters_get","tags":["Customer Meters"],"x-fern-sdk-group-name":["customerMeters"],"summary":"Retrieve a customer-meter aggregate","description":"Returns a single customer-meter aggregate for the specified customer + meter pair. Returns 404 only if the meter itself does not exist on the merchant — if the meter exists but the customer has zero events, the response returns an aggregate with `total_quantity: 0`, `event_count: 0`, `last_event_at: null`.\n\n---\n\n**Related endpoints**\n\n- `GET /customer-meters` — List a customer's meter usage\n\n**Common errors**\n\n- `401 authentication_error` — missing, invalid, expired, or revoked credential. Codes: `authentication_failed`, `invalid_api_key`, `expired_api_key`, `api_key_revoked`, `session_invalid`, `merchant_required`. Carries a `WWW-Authenticate: Bearer` challenge.\n- `404 resource_missing` — the referenced resource does not exist or is not visible to your key.","parameters":[{"schema":{"type":"string","format":"uuid","description":"Customer UUID"},"required":true,"description":"Customer UUID","name":"customer_id","in":"path"},{"schema":{"type":"string","format":"uuid","description":"Meter UUID"},"required":true,"description":"Meter UUID","name":"meter_id","in":"path"}],"responses":{"200":{"description":"Customer-meter aggregate.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CustomerMeterResponse"}}}},"401":{"description":"Unauthorized.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CustomerMeterErrorResponse"}}},"headers":{"WWW-Authenticate":{"description":"RFC 6750 Bearer authentication challenge","schema":{"type":"string"},"example":"Bearer realm=\"revkeen-api\""}}},"404":{"description":"Meter not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CustomerMeterErrorResponse"}}}},"429":{"description":"Too Many Requests - Rate limit exceeded","headers":{"Retry-After":{"description":"Whole seconds until the caller may retry","schema":{"type":"string"},"example":"60"},"X-RateLimit-Limit":{"description":"Request quota for the current window","schema":{"type":"string"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window","schema":{"type":"string"}},"X-RateLimit-Reset":{"description":"UNIX timestamp in seconds when the window resets","schema":{"type":"string"}}}}},"x-cli-group":"customer-meters","x-cli-name":"get","x-codeSamples":[{"lang":"curl","label":"cURL","source":"curl -X GET \"https://api.revkeen.com/v2/customer-meters/11111111-1111-4111-8111-111111111111/11111111-1111-4111-8111-111111111111\" \\\n  -H \"x-api-key: $REVKEEN_API_KEY\""},{"lang":"typescript","label":"TypeScript (SDK)","source":"// npm install @revkeen/sdk@1.20260822.1541\nimport { RevKeenClient } from \"@revkeen/sdk\";\nconst apiKey = process.env.REVKEEN_API_KEY;\nif (!apiKey) throw new Error(\"Set REVKEEN_API_KEY\");\nconst client = new RevKeenClient({ apiKey, baseUrl: \"https://api.revkeen.com/v2\" });\nconst result = await client.customer.metersGet({\n  \"customer_id\": \"11111111-1111-4111-8111-111111111111\",\n  \"meter_id\": \"11111111-1111-4111-8111-111111111111\"\n});\nconsole.log(result);"},{"lang":"go","label":"Go (HTTP)","source":"package main\nimport (\"fmt\"; \"io\"; \"net/http\"; \"os\"; \"strings\"; \"time\")\nfunc main() {\n  req, err := http.NewRequest(\"GET\", \"https://api.revkeen.com/v2/customer-meters/11111111-1111-4111-8111-111111111111/11111111-1111-4111-8111-111111111111\", strings.NewReader(\"\"))\n  if err != nil { panic(err) }\n  req.Header.Set(\"x-api-key\", os.Getenv(\"REVKEEN_API_KEY\"))\n  req.Header.Set(\"Content-Type\", \"application/json\")\n  client := &http.Client{Timeout: 10 * time.Second}\n  response, err := client.Do(req)\n  if err != nil { panic(err) }\n  defer response.Body.Close()\n  if response.StatusCode >= 400 { panic(fmt.Sprintf(\"RevKeen request failed (%d)\", response.StatusCode)) }\n  data, err := io.ReadAll(response.Body)\n  if err != nil { panic(err) }\n  fmt.Println(string(data))\n}"},{"lang":"php","label":"PHP (HTTP)","source":"<?php\n$request = curl_init(\"https://api.revkeen.com/v2/customer-meters/11111111-1111-4111-8111-111111111111/11111111-1111-4111-8111-111111111111\");\ncurl_setopt_array($request, [\n  CURLOPT_CUSTOMREQUEST => \"GET\",\n  CURLOPT_RETURNTRANSFER => true,\n  CURLOPT_TIMEOUT => 10,\n  CURLOPT_HTTPHEADER => [\"x-api-key: \" . getenv(\"REVKEEN_API_KEY\"), \"Content-Type: application/json\"],\n]);\n$response = curl_exec($request);\nif ($response === false) throw new RuntimeException(curl_error($request));\n$status = curl_getinfo($request, CURLINFO_HTTP_CODE);\ncurl_close($request);\nif ($status >= 400) throw new RuntimeException(\"RevKeen request failed ($status)\");\necho $response;"},{"lang":"python","label":"Python","source":"import os\n\nimport requests\n\nresponse = requests.get(\n    \"https://api.revkeen.com/v2/customer-meters/11111111-1111-4111-8111-111111111111/11111111-1111-4111-8111-111111111111\",\n    headers={\"x-api-key\": os.environ[\"REVKEEN_API_KEY\"]},\n)\n\nresponse.raise_for_status()\nprint(response.json())"},{"lang":"javascript","label":"JavaScript","source":"const credential = process.env.REVKEEN_API_KEY;\nif (!credential) throw new Error(\"Set REVKEEN_API_KEY\");\nconst response = await fetch(\"https://api.revkeen.com/v2/customer-meters/11111111-1111-4111-8111-111111111111/11111111-1111-4111-8111-111111111111\", {\n  method: \"GET\",\n  headers: {\n    \"x-api-key\": credential,\n  },\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"java","label":"Java","source":"import java.net.URI;\nimport java.net.http.HttpClient;\nimport java.net.http.HttpRequest;\nimport java.net.http.HttpResponse;\n\nHttpClient client = HttpClient.newHttpClient();\n\nHttpRequest request = HttpRequest.newBuilder()\n    .uri(URI.create(\"https://api.revkeen.com/v2/customer-meters/11111111-1111-4111-8111-111111111111/11111111-1111-4111-8111-111111111111\"))\n    .header(\"x-api-key\", System.getenv(\"REVKEEN_API_KEY\"))\n    .GET()\n    .build();\n\nHttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());\nSystem.out.println(response.body());"},{"lang":"ruby","label":"Ruby","source":"require \"net/http\"\nrequire \"json\"\nrequire \"uri\"\n\nuri = URI(\"https://api.revkeen.com/v2/customer-meters/11111111-1111-4111-8111-111111111111/11111111-1111-4111-8111-111111111111\")\nhttp = Net::HTTP.new(uri.host, uri.port)\nhttp.use_ssl = true\n\nrequest = Net::HTTP::Get.new(uri)\nrequest[\"x-api-key\"] = ENV[\"REVKEEN_API_KEY\"]\n\nresponse = http.request(request)\nputs response.body"}],"security":[{"apiKey":["meters:read"]},{"oauth2":["meters:read"]}]}},"/payment-intents":{"post":{"operationId":"payment_intents_create","tags":["Payment Intents"],"x-fern-sdk-group-name":["payment_intents"],"x-codeSamples":[{"lang":"curl","label":"cURL","source":"curl -X POST \"https://api.revkeen.com/v2/payment-intents\" \\\n  -H \"x-api-key: $REVKEEN_API_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"amount\": 5000,\n    \"customer\": \"11111111-1111-4111-8111-111111111111\"\n  }'"},{"lang":"typescript","label":"TypeScript (SDK)","source":"// npm install @revkeen/sdk@1.20260822.1541\nimport { RevKeenClient } from \"@revkeen/sdk\";\nconst apiKey = process.env.REVKEEN_API_KEY;\nif (!apiKey) throw new Error(\"Set REVKEEN_API_KEY\");\nconst client = new RevKeenClient({ apiKey, baseUrl: \"https://api.revkeen.com/v2\" });\nconst result = await client.paymentIntents.create({\n  \"amount\": 5000,\n  \"customer\": \"11111111-1111-4111-8111-111111111111\"\n});\nconsole.log(result);"},{"lang":"go","label":"Go (HTTP)","source":"package main\nimport (\"fmt\"; \"io\"; \"net/http\"; \"os\"; \"strings\"; \"time\")\nfunc main() {\n  req, err := http.NewRequest(\"POST\", \"https://api.revkeen.com/v2/payment-intents\", strings.NewReader(\"{\\\"amount\\\":5000,\\\"customer\\\":\\\"11111111-1111-4111-8111-111111111111\\\"}\"))\n  if err != nil { panic(err) }\n  req.Header.Set(\"x-api-key\", os.Getenv(\"REVKEEN_API_KEY\"))\n  req.Header.Set(\"Content-Type\", \"application/json\")\n  client := &http.Client{Timeout: 10 * time.Second}\n  response, err := client.Do(req)\n  if err != nil { panic(err) }\n  defer response.Body.Close()\n  if response.StatusCode >= 400 { panic(fmt.Sprintf(\"RevKeen request failed (%d)\", response.StatusCode)) }\n  data, err := io.ReadAll(response.Body)\n  if err != nil { panic(err) }\n  fmt.Println(string(data))\n}"},{"lang":"php","label":"PHP (HTTP)","source":"<?php\n$request = curl_init(\"https://api.revkeen.com/v2/payment-intents\");\ncurl_setopt_array($request, [\n  CURLOPT_CUSTOMREQUEST => \"POST\",\n  CURLOPT_RETURNTRANSFER => true,\n  CURLOPT_TIMEOUT => 10,\n  CURLOPT_HTTPHEADER => [\"x-api-key: \" . getenv(\"REVKEEN_API_KEY\"), \"Content-Type: application/json\"],\n  CURLOPT_POSTFIELDS => '{\\\"amount\\\":5000,\\\"customer\\\":\\\"11111111-1111-4111-8111-111111111111\\\"}',\n]);\n$response = curl_exec($request);\nif ($response === false) throw new RuntimeException(curl_error($request));\n$status = curl_getinfo($request, CURLINFO_HTTP_CODE);\ncurl_close($request);\nif ($status >= 400) throw new RuntimeException(\"RevKeen request failed ($status)\");\necho $response;"},{"lang":"python","label":"Python","source":"import os\n\nimport requests\n\nresponse = requests.post(\n    \"https://api.revkeen.com/v2/payment-intents\",\n    headers={\"x-api-key\": os.environ[\"REVKEEN_API_KEY\"]},\n    json={\n        \"amount\": 5000,\n        \"customer\": \"11111111-1111-4111-8111-111111111111\",\n    },\n)\n\nresponse.raise_for_status()\nprint(response.json())"},{"lang":"javascript","label":"JavaScript","source":"const credential = process.env.REVKEEN_API_KEY;\nif (!credential) throw new Error(\"Set REVKEEN_API_KEY\");\nconst response = await fetch(\"https://api.revkeen.com/v2/payment-intents\", {\n  method: \"POST\",\n  headers: {\n    \"x-api-key\": credential,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n    \"amount\": 5000,\n    \"customer\": \"11111111-1111-4111-8111-111111111111\"\n  }),\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"java","label":"Java","source":"import java.net.URI;\nimport java.net.http.HttpClient;\nimport java.net.http.HttpRequest;\nimport java.net.http.HttpResponse;\n\nHttpClient client = HttpClient.newHttpClient();\n\nHttpRequest request = HttpRequest.newBuilder()\n    .uri(URI.create(\"https://api.revkeen.com/v2/payment-intents\"))\n    .header(\"x-api-key\", System.getenv(\"REVKEEN_API_KEY\"))\n    .header(\"Content-Type\", \"application/json\")\n    .method(\"POST\", HttpRequest.BodyPublishers.ofString(\"\"\"\n        {\n          \"amount\": 5000,\n          \"customer\": \"11111111-1111-4111-8111-111111111111\"\n        }\n        \"\"\"))\n    .build();\n\nHttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());\nSystem.out.println(response.body());"},{"lang":"ruby","label":"Ruby","source":"require \"net/http\"\nrequire \"json\"\nrequire \"uri\"\n\nuri = URI(\"https://api.revkeen.com/v2/payment-intents\")\nhttp = Net::HTTP.new(uri.host, uri.port)\nhttp.use_ssl = true\n\nrequest = Net::HTTP::Post.new(uri)\nrequest[\"x-api-key\"] = ENV[\"REVKEEN_API_KEY\"]\nrequest[\"Content-Type\"] = \"application/json\"\nrequest.body = {\n  \"amount\" => 5000,\n  \"customer\" => \"11111111-1111-4111-8111-111111111111\",\n}.to_json\n\nresponse = http.request(request)\nputs response.body"}],"summary":"Create a payment intent","description":"Creates a payment intent to orchestrate payment collection with support for 3DS/SCA authentication.\n\n---\n\n**Related endpoints**\n\n- `GET /payment-intents` — List payment intents\n- `GET /payment-intents/{id}` — Retrieve a payment intent\n- `POST /payment-intents/{id}/confirm` — Confirm a payment intent\n- `POST /payment-intents/{id}/capture` — Capture a payment intent\n- `POST /payment-intents/{id}/cancel` — Cancel a payment intent\n\n**Common errors**\n\n- `400 invalid_request` — malformed payload or failed validation.\n- `401 authentication_error` — missing, invalid, expired, or revoked credential. Codes: `authentication_failed`, `invalid_api_key`, `expired_api_key`, `api_key_revoked`, `session_invalid`, `merchant_required`. Carries a `WWW-Authenticate: Bearer` challenge.\n- `404 resource_missing` — the referenced resource does not exist or is not visible to your key.\n\n**Idempotency**\n\nPass an `Idempotency-Key` header (UUID v4 recommended) to make retries safe. Keys are valid for 24 hours; see [the idempotency guide](/docs/fundamentals/idempotency).","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreatePaymentIntentRequest"}}}},"responses":{"200":{"description":"Payment intent created successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaymentIntent"}}}},"400":{"description":"Invalid request. `capture_method_unsupported`: `capture_method` is `manual`, which is not supported yet (a payment intent is charged in full when it is confirmed).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaymentIntentErrorResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaymentIntentErrorResponse"}}},"headers":{"WWW-Authenticate":{"description":"RFC 6750 Bearer authentication challenge","schema":{"type":"string"},"example":"Bearer realm=\"revkeen-api\""}}},"404":{"description":"`resource_missing`: no customer with this id for the merchant","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaymentIntentErrorResponse"}}}},"429":{"description":"Too Many Requests - Rate limit exceeded","headers":{"Retry-After":{"description":"Whole seconds until the caller may retry","schema":{"type":"string"},"example":"60"},"X-RateLimit-Limit":{"description":"Request quota for the current window","schema":{"type":"string"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window","schema":{"type":"string"}},"X-RateLimit-Reset":{"description":"UNIX timestamp in seconds when the window resets","schema":{"type":"string"}}}}},"x-cli-group":"payment-intents","x-cli-name":"create","security":[{"apiKey":["payment_intents:write"]},{"oauth2":["payment_intents:write"]}]},"get":{"operationId":"payment_intents_list","tags":["Payment Intents"],"x-fern-sdk-group-name":["payment_intents"],"x-codeSamples":[{"lang":"curl","label":"cURL","source":"curl -X GET \"https://api.revkeen.com/v2/payment-intents?customer=11111111-1111-4111-8111-111111111111&status=requires_payment_method\" \\\n  -H \"x-api-key: $REVKEEN_API_KEY\""},{"lang":"typescript","label":"TypeScript (SDK)","source":"// npm install @revkeen/sdk@1.20260822.1541\nimport { RevKeenClient } from \"@revkeen/sdk\";\nconst apiKey = process.env.REVKEEN_API_KEY;\nif (!apiKey) throw new Error(\"Set REVKEEN_API_KEY\");\nconst client = new RevKeenClient({ apiKey, baseUrl: \"https://api.revkeen.com/v2\" });\nconst result = await client.paymentIntents.list({\n  \"customer\": \"11111111-1111-4111-8111-111111111111\",\n  \"status\": \"requires_payment_method\"\n});\nconsole.log(result);"},{"lang":"go","label":"Go (HTTP)","source":"package main\nimport (\"fmt\"; \"io\"; \"net/http\"; \"os\"; \"strings\"; \"time\")\nfunc main() {\n  req, err := http.NewRequest(\"GET\", \"https://api.revkeen.com/v2/payment-intents?customer=11111111-1111-4111-8111-111111111111&status=requires_payment_method\", strings.NewReader(\"\"))\n  if err != nil { panic(err) }\n  req.Header.Set(\"x-api-key\", os.Getenv(\"REVKEEN_API_KEY\"))\n  req.Header.Set(\"Content-Type\", \"application/json\")\n  client := &http.Client{Timeout: 10 * time.Second}\n  response, err := client.Do(req)\n  if err != nil { panic(err) }\n  defer response.Body.Close()\n  if response.StatusCode >= 400 { panic(fmt.Sprintf(\"RevKeen request failed (%d)\", response.StatusCode)) }\n  data, err := io.ReadAll(response.Body)\n  if err != nil { panic(err) }\n  fmt.Println(string(data))\n}"},{"lang":"php","label":"PHP (HTTP)","source":"<?php\n$request = curl_init(\"https://api.revkeen.com/v2/payment-intents?customer=11111111-1111-4111-8111-111111111111&status=requires_payment_method\");\ncurl_setopt_array($request, [\n  CURLOPT_CUSTOMREQUEST => \"GET\",\n  CURLOPT_RETURNTRANSFER => true,\n  CURLOPT_TIMEOUT => 10,\n  CURLOPT_HTTPHEADER => [\"x-api-key: \" . getenv(\"REVKEEN_API_KEY\"), \"Content-Type: application/json\"],\n]);\n$response = curl_exec($request);\nif ($response === false) throw new RuntimeException(curl_error($request));\n$status = curl_getinfo($request, CURLINFO_HTTP_CODE);\ncurl_close($request);\nif ($status >= 400) throw new RuntimeException(\"RevKeen request failed ($status)\");\necho $response;"},{"lang":"python","label":"Python","source":"import os\n\nimport requests\n\nresponse = requests.get(\n    \"https://api.revkeen.com/v2/payment-intents\",\n    headers={\"x-api-key\": os.environ[\"REVKEEN_API_KEY\"]},\n    params={\n        \"customer\": \"11111111-1111-4111-8111-111111111111\",\n        \"status\": \"requires_payment_method\",\n    },\n)\n\nresponse.raise_for_status()\nprint(response.json())"},{"lang":"javascript","label":"JavaScript","source":"const credential = process.env.REVKEEN_API_KEY;\nif (!credential) throw new Error(\"Set REVKEEN_API_KEY\");\nconst response = await fetch(\"https://api.revkeen.com/v2/payment-intents?customer=11111111-1111-4111-8111-111111111111&status=requires_payment_method\", {\n  method: \"GET\",\n  headers: {\n    \"x-api-key\": credential,\n  },\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"java","label":"Java","source":"import java.net.URI;\nimport java.net.http.HttpClient;\nimport java.net.http.HttpRequest;\nimport java.net.http.HttpResponse;\n\nHttpClient client = HttpClient.newHttpClient();\n\nHttpRequest request = HttpRequest.newBuilder()\n    .uri(URI.create(\"https://api.revkeen.com/v2/payment-intents?customer=11111111-1111-4111-8111-111111111111&status=requires_payment_method\"))\n    .header(\"x-api-key\", System.getenv(\"REVKEEN_API_KEY\"))\n    .GET()\n    .build();\n\nHttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());\nSystem.out.println(response.body());"},{"lang":"ruby","label":"Ruby","source":"require \"net/http\"\nrequire \"json\"\nrequire \"uri\"\n\nuri = URI(\"https://api.revkeen.com/v2/payment-intents?customer=11111111-1111-4111-8111-111111111111&status=requires_payment_method\")\nhttp = Net::HTTP.new(uri.host, uri.port)\nhttp.use_ssl = true\n\nrequest = Net::HTTP::Get.new(uri)\nrequest[\"x-api-key\"] = ENV[\"REVKEEN_API_KEY\"]\n\nresponse = http.request(request)\nputs response.body"}],"summary":"List payment intents","description":"Returns a list of payment intents with optional filtering.\n\n---\n\n**Related endpoints**\n\n- `POST /payment-intents` — Create a payment intent\n- `GET /payment-intents/{id}` — Retrieve a payment intent\n- `POST /payment-intents/{id}/confirm` — Confirm a payment intent\n- `POST /payment-intents/{id}/capture` — Capture a payment intent\n- `POST /payment-intents/{id}/cancel` — Cancel a payment intent\n\n**Common errors**\n\n- `400 invalid_request` — malformed payload or failed validation.\n- `401 authentication_error` — missing, invalid, expired, or revoked credential. Codes: `authentication_failed`, `invalid_api_key`, `expired_api_key`, `api_key_revoked`, `session_invalid`, `merchant_required`. Carries a `WWW-Authenticate: Bearer` challenge.\n\n**Pagination**\n\nOffset-based with `limit` (default 25, max 100) and `offset`. The response `pagination` block includes `total` and `hasMore`. See [the pagination guide](/docs/fundamentals/pagination) for SDK auto-paging helpers.","parameters":[{"schema":{"type":"string","format":"uuid","description":"Filter by customer ID"},"required":false,"description":"Filter by customer ID","name":"customer","in":"query"},{"schema":{"type":"string","enum":["requires_payment_method","requires_confirmation","requires_action","processing","succeeded","canceled"],"description":"Filter by status"},"required":false,"description":"Filter by status","name":"status","in":"query"},{"schema":{"type":["number","null"],"minimum":0,"maximum":253402300799,"description":"Filter by created_at >= (Unix timestamp, seconds)"},"required":false,"description":"Filter by created_at >= (Unix timestamp, seconds)","name":"created_gte","in":"query"},{"schema":{"type":["number","null"],"minimum":0,"maximum":253402300799,"description":"Filter by created_at <= (Unix timestamp, seconds)"},"required":false,"description":"Filter by created_at <= (Unix timestamp, seconds)","name":"created_lte","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":100,"default":20,"description":"Maximum number of results (1-100)"},"required":false,"description":"Maximum number of results (1-100)","name":"limit","in":"query"},{"schema":{"type":"string","description":"Cursor for pagination - return results after this ID (pi_xxx)"},"required":false,"description":"Cursor for pagination - return results after this ID (pi_xxx)","name":"starting_after","in":"query"},{"schema":{"type":"string","description":"Cursor for pagination - return results before this ID (pi_xxx)"},"required":false,"description":"Cursor for pagination - return results before this ID (pi_xxx)","name":"ending_before","in":"query"}],"responses":{"200":{"description":"List of payment intents","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaymentIntentListResponse"}}}},"400":{"description":"A query parameter failed validation; `param` names it. Combining `starting_after` with `ending_before`, or a cursor that names no payment intent of this merchant, is also a 400 (`validation_error`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaymentIntentListValidationError"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaymentIntentErrorResponse"}}},"headers":{"WWW-Authenticate":{"description":"RFC 6750 Bearer authentication challenge","schema":{"type":"string"},"example":"Bearer realm=\"revkeen-api\""}}},"429":{"description":"Too Many Requests - Rate limit exceeded","headers":{"Retry-After":{"description":"Whole seconds until the caller may retry","schema":{"type":"string"},"example":"60"},"X-RateLimit-Limit":{"description":"Request quota for the current window","schema":{"type":"string"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window","schema":{"type":"string"}},"X-RateLimit-Reset":{"description":"UNIX timestamp in seconds when the window resets","schema":{"type":"string"}}}}},"x-cli-group":"payment-intents","x-cli-name":"list","security":[{"apiKey":["payment_intents:read"]},{"oauth2":["payment_intents:read"]}]}},"/payment-intents/{id}":{"get":{"operationId":"payment_intents_get","tags":["Payment Intents"],"x-fern-sdk-group-name":["payment_intents"],"x-codeSamples":[{"lang":"curl","label":"cURL","source":"curl -X GET \"https://api.revkeen.com/v2/payment-intents/pi_abc123\" \\\n  -H \"x-api-key: $REVKEEN_API_KEY\""},{"lang":"typescript","label":"TypeScript (SDK)","source":"// npm install @revkeen/sdk@1.20260822.1541\nimport { RevKeenClient } from \"@revkeen/sdk\";\nconst apiKey = process.env.REVKEEN_API_KEY;\nif (!apiKey) throw new Error(\"Set REVKEEN_API_KEY\");\nconst client = new RevKeenClient({ apiKey, baseUrl: \"https://api.revkeen.com/v2\" });\nconst result = await client.paymentIntents.get(\"pi_abc123\");\nconsole.log(result);"},{"lang":"go","label":"Go (HTTP)","source":"package main\nimport (\"fmt\"; \"io\"; \"net/http\"; \"os\"; \"strings\"; \"time\")\nfunc main() {\n  req, err := http.NewRequest(\"GET\", \"https://api.revkeen.com/v2/payment-intents/pi_abc123\", strings.NewReader(\"\"))\n  if err != nil { panic(err) }\n  req.Header.Set(\"x-api-key\", os.Getenv(\"REVKEEN_API_KEY\"))\n  req.Header.Set(\"Content-Type\", \"application/json\")\n  client := &http.Client{Timeout: 10 * time.Second}\n  response, err := client.Do(req)\n  if err != nil { panic(err) }\n  defer response.Body.Close()\n  if response.StatusCode >= 400 { panic(fmt.Sprintf(\"RevKeen request failed (%d)\", response.StatusCode)) }\n  data, err := io.ReadAll(response.Body)\n  if err != nil { panic(err) }\n  fmt.Println(string(data))\n}"},{"lang":"php","label":"PHP (HTTP)","source":"<?php\n$request = curl_init(\"https://api.revkeen.com/v2/payment-intents/pi_abc123\");\ncurl_setopt_array($request, [\n  CURLOPT_CUSTOMREQUEST => \"GET\",\n  CURLOPT_RETURNTRANSFER => true,\n  CURLOPT_TIMEOUT => 10,\n  CURLOPT_HTTPHEADER => [\"x-api-key: \" . getenv(\"REVKEEN_API_KEY\"), \"Content-Type: application/json\"],\n]);\n$response = curl_exec($request);\nif ($response === false) throw new RuntimeException(curl_error($request));\n$status = curl_getinfo($request, CURLINFO_HTTP_CODE);\ncurl_close($request);\nif ($status >= 400) throw new RuntimeException(\"RevKeen request failed ($status)\");\necho $response;"},{"lang":"python","label":"Python","source":"import os\n\nimport requests\n\nresponse = requests.get(\n    \"https://api.revkeen.com/v2/payment-intents/pi_abc123\",\n    headers={\"x-api-key\": os.environ[\"REVKEEN_API_KEY\"]},\n)\n\nresponse.raise_for_status()\nprint(response.json())"},{"lang":"javascript","label":"JavaScript","source":"const credential = process.env.REVKEEN_API_KEY;\nif (!credential) throw new Error(\"Set REVKEEN_API_KEY\");\nconst response = await fetch(\"https://api.revkeen.com/v2/payment-intents/pi_abc123\", {\n  method: \"GET\",\n  headers: {\n    \"x-api-key\": credential,\n  },\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"java","label":"Java","source":"import java.net.URI;\nimport java.net.http.HttpClient;\nimport java.net.http.HttpRequest;\nimport java.net.http.HttpResponse;\n\nHttpClient client = HttpClient.newHttpClient();\n\nHttpRequest request = HttpRequest.newBuilder()\n    .uri(URI.create(\"https://api.revkeen.com/v2/payment-intents/pi_abc123\"))\n    .header(\"x-api-key\", System.getenv(\"REVKEEN_API_KEY\"))\n    .GET()\n    .build();\n\nHttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());\nSystem.out.println(response.body());"},{"lang":"ruby","label":"Ruby","source":"require \"net/http\"\nrequire \"json\"\nrequire \"uri\"\n\nuri = URI(\"https://api.revkeen.com/v2/payment-intents/pi_abc123\")\nhttp = Net::HTTP.new(uri.host, uri.port)\nhttp.use_ssl = true\n\nrequest = Net::HTTP::Get.new(uri)\nrequest[\"x-api-key\"] = ENV[\"REVKEEN_API_KEY\"]\n\nresponse = http.request(request)\nputs response.body"}],"summary":"Retrieve a payment intent","description":"Retrieves a payment intent by its ID (pi_xxx).\n\n---\n\n**Related endpoints**\n\n- `POST /payment-intents` — Create a payment intent\n- `GET /payment-intents` — List payment intents\n- `POST /payment-intents/{id}/confirm` — Confirm a payment intent\n- `POST /payment-intents/{id}/capture` — Capture a payment intent\n- `POST /payment-intents/{id}/cancel` — Cancel a payment intent\n\n**Common errors**\n\n- `400 invalid_request` — malformed payload or failed validation.\n- `404 resource_missing` — the referenced resource does not exist or is not visible to your key.","parameters":[{"schema":{"type":"string","pattern":"^(?:pi_[A-Za-z0-9]+|[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12})$","description":"Payment intent public ID (`pi_…`) or UUID.","example":"pi_abc123"},"required":true,"description":"Payment intent public ID (`pi_…`) or UUID.","name":"id","in":"path"}],"responses":{"200":{"description":"Payment intent details","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaymentIntent"}}}},"400":{"description":"`id` is neither a `pi_…` public ID nor a UUID","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaymentIntentRetrieveValidationError"}}}},"401":{"description":"Unauthorized - Invalid or missing API key","headers":{"WWW-Authenticate":{"description":"RFC 6750 Bearer authentication challenge","schema":{"type":"string"},"example":"Bearer realm=\"revkeen-api\""}}},"404":{"description":"Payment intent not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaymentIntentErrorResponse"}}}},"429":{"description":"Too Many Requests - Rate limit exceeded","headers":{"Retry-After":{"description":"Whole seconds until the caller may retry","schema":{"type":"string"},"example":"60"},"X-RateLimit-Limit":{"description":"Request quota for the current window","schema":{"type":"string"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window","schema":{"type":"string"}},"X-RateLimit-Reset":{"description":"UNIX timestamp in seconds when the window resets","schema":{"type":"string"}}}}},"x-cli-group":"payment-intents","x-cli-name":"get","security":[{"apiKey":["payment_intents:read"]},{"oauth2":["payment_intents:read"]}]}},"/payment-intents/{id}/confirm":{"post":{"operationId":"payment_intents_confirm","tags":["Payment Intents"],"x-fern-sdk-group-name":["payment_intents"],"x-codeSamples":[{"lang":"curl","label":"cURL","source":"curl -X POST \"https://api.revkeen.com/v2/payment-intents/pi_abc123/confirm\" \\\n  -H \"x-api-key: $REVKEEN_API_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"payment_method\": \"11111111-1111-4111-8111-111111111111\",\n    \"return_url\": \"https://example.com/success\"\n  }'"},{"lang":"typescript","label":"TypeScript (SDK)","source":"// npm install @revkeen/sdk@1.20260822.1541\nimport { RevKeenClient } from \"@revkeen/sdk\";\nconst apiKey = process.env.REVKEEN_API_KEY;\nif (!apiKey) throw new Error(\"Set REVKEEN_API_KEY\");\nconst client = new RevKeenClient({ apiKey, baseUrl: \"https://api.revkeen.com/v2\" });\nconst result = await client.paymentIntents.confirm(\"pi_abc123\", {\n  \"payment_method\": \"11111111-1111-4111-8111-111111111111\",\n  \"return_url\": \"https://example.com/success\"\n});\nconsole.log(result);"},{"lang":"go","label":"Go (HTTP)","source":"package main\nimport (\"fmt\"; \"io\"; \"net/http\"; \"os\"; \"strings\"; \"time\")\nfunc main() {\n  req, err := http.NewRequest(\"POST\", \"https://api.revkeen.com/v2/payment-intents/pi_abc123/confirm\", strings.NewReader(\"{\\\"payment_method\\\":\\\"11111111-1111-4111-8111-111111111111\\\",\\\"return_url\\\":\\\"https://example.com/success\\\"}\"))\n  if err != nil { panic(err) }\n  req.Header.Set(\"x-api-key\", os.Getenv(\"REVKEEN_API_KEY\"))\n  req.Header.Set(\"Content-Type\", \"application/json\")\n  client := &http.Client{Timeout: 10 * time.Second}\n  response, err := client.Do(req)\n  if err != nil { panic(err) }\n  defer response.Body.Close()\n  if response.StatusCode >= 400 { panic(fmt.Sprintf(\"RevKeen request failed (%d)\", response.StatusCode)) }\n  data, err := io.ReadAll(response.Body)\n  if err != nil { panic(err) }\n  fmt.Println(string(data))\n}"},{"lang":"php","label":"PHP (HTTP)","source":"<?php\n$request = curl_init(\"https://api.revkeen.com/v2/payment-intents/pi_abc123/confirm\");\ncurl_setopt_array($request, [\n  CURLOPT_CUSTOMREQUEST => \"POST\",\n  CURLOPT_RETURNTRANSFER => true,\n  CURLOPT_TIMEOUT => 10,\n  CURLOPT_HTTPHEADER => [\"x-api-key: \" . getenv(\"REVKEEN_API_KEY\"), \"Content-Type: application/json\"],\n  CURLOPT_POSTFIELDS => '{\\\"payment_method\\\":\\\"11111111-1111-4111-8111-111111111111\\\",\\\"return_url\\\":\\\"https://example.com/success\\\"}',\n]);\n$response = curl_exec($request);\nif ($response === false) throw new RuntimeException(curl_error($request));\n$status = curl_getinfo($request, CURLINFO_HTTP_CODE);\ncurl_close($request);\nif ($status >= 400) throw new RuntimeException(\"RevKeen request failed ($status)\");\necho $response;"},{"lang":"python","label":"Python","source":"import os\n\nimport requests\n\nresponse = requests.post(\n    \"https://api.revkeen.com/v2/payment-intents/pi_abc123/confirm\",\n    headers={\"x-api-key\": os.environ[\"REVKEEN_API_KEY\"]},\n    json={\n        \"payment_method\": \"11111111-1111-4111-8111-111111111111\",\n        \"return_url\": \"https://example.com/success\",\n    },\n)\n\nresponse.raise_for_status()\nprint(response.json())"},{"lang":"javascript","label":"JavaScript","source":"const credential = process.env.REVKEEN_API_KEY;\nif (!credential) throw new Error(\"Set REVKEEN_API_KEY\");\nconst response = await fetch(\"https://api.revkeen.com/v2/payment-intents/pi_abc123/confirm\", {\n  method: \"POST\",\n  headers: {\n    \"x-api-key\": credential,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n    \"payment_method\": \"11111111-1111-4111-8111-111111111111\",\n    \"return_url\": \"https://example.com/success\"\n  }),\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"java","label":"Java","source":"import java.net.URI;\nimport java.net.http.HttpClient;\nimport java.net.http.HttpRequest;\nimport java.net.http.HttpResponse;\n\nHttpClient client = HttpClient.newHttpClient();\n\nHttpRequest request = HttpRequest.newBuilder()\n    .uri(URI.create(\"https://api.revkeen.com/v2/payment-intents/pi_abc123/confirm\"))\n    .header(\"x-api-key\", System.getenv(\"REVKEEN_API_KEY\"))\n    .header(\"Content-Type\", \"application/json\")\n    .method(\"POST\", HttpRequest.BodyPublishers.ofString(\"\"\"\n        {\n          \"payment_method\": \"11111111-1111-4111-8111-111111111111\",\n          \"return_url\": \"https://example.com/success\"\n        }\n        \"\"\"))\n    .build();\n\nHttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());\nSystem.out.println(response.body());"},{"lang":"ruby","label":"Ruby","source":"require \"net/http\"\nrequire \"json\"\nrequire \"uri\"\n\nuri = URI(\"https://api.revkeen.com/v2/payment-intents/pi_abc123/confirm\")\nhttp = Net::HTTP.new(uri.host, uri.port)\nhttp.use_ssl = true\n\nrequest = Net::HTTP::Post.new(uri)\nrequest[\"x-api-key\"] = ENV[\"REVKEEN_API_KEY\"]\nrequest[\"Content-Type\"] = \"application/json\"\nrequest.body = {\n  \"payment_method\" => \"11111111-1111-4111-8111-111111111111\",\n  \"return_url\" => \"https://example.com/success\",\n}.to_json\n\nresponse = http.request(request)\nputs response.body"}],"summary":"Confirm a payment intent","description":"Not available through the API yet. There is no way to supply a card token or a saved card here, so a confirm cannot charge: it returns 422 `payment_method_unusable` and leaves the payment intent unchanged. Take card payments with hosted checkout or a payment link. A server-side confirm for saved cards is planned.\n\n---\n\n**Related endpoints**\n\n- `POST /payment-intents` — Create a payment intent\n- `GET /payment-intents` — List payment intents\n- `GET /payment-intents/{id}` — Retrieve a payment intent\n- `POST /payment-intents/{id}/capture` — Capture a payment intent\n- `POST /payment-intents/{id}/cancel` — Cancel a payment intent\n\n**Common errors**\n\n- `400 invalid_request` — malformed payload or failed validation.\n- `404 resource_missing` — the referenced resource does not exist or is not visible to your key.\n- `422 unprocessable_entity` — business-rule failure (for example, refunding more than the original charge).\n\n**Idempotency**\n\nPass an `Idempotency-Key` header (UUID v4 recommended) to make retries safe. Keys are valid for 24 hours; see [the idempotency guide](/docs/fundamentals/idempotency).","parameters":[{"schema":{"type":"string","pattern":"^(?:pi_[A-Za-z0-9]+|[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12})$","description":"Payment intent public ID (`pi_…`) or UUID.","example":"pi_abc123"},"required":true,"description":"Payment intent public ID (`pi_…`) or UUID.","name":"id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConfirmPaymentIntentRequest"}}}},"responses":{"200":{"description":"Confirmed payment intent","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaymentIntent"}}}},"400":{"description":"Invalid request or confirmation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaymentIntentErrorResponse"}}}},"401":{"description":"Unauthorized - Invalid or missing API key","headers":{"WWW-Authenticate":{"description":"RFC 6750 Bearer authentication challenge","schema":{"type":"string"},"example":"Bearer realm=\"revkeen-api\""}}},"404":{"description":"Payment intent not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaymentIntentErrorResponse"}}}},"422":{"description":"`payment_method_unusable`: the API cannot confirm a payment intent yet. The intent is unchanged","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaymentIntentErrorResponse"}}}},"429":{"description":"Too Many Requests - Rate limit exceeded","headers":{"Retry-After":{"description":"Whole seconds until the caller may retry","schema":{"type":"string"},"example":"60"},"X-RateLimit-Limit":{"description":"Request quota for the current window","schema":{"type":"string"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window","schema":{"type":"string"}},"X-RateLimit-Reset":{"description":"UNIX timestamp in seconds when the window resets","schema":{"type":"string"}}}}},"x-cli-group":"payment-intents","x-cli-name":"confirm","security":[{"apiKey":["payment_intents:write"]},{"oauth2":["payment_intents:write"]}]}},"/payment-intents/{id}/capture":{"post":{"operationId":"payment_intents_capture","tags":["Payment Intents"],"x-fern-sdk-group-name":["payment_intents"],"x-codeSamples":[{"lang":"curl","label":"cURL","source":"curl -X POST \"https://api.revkeen.com/v2/payment-intents/pi_abc123/capture\" \\\n  -H \"x-api-key: $REVKEEN_API_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"amount_to_capture\": 1000\n  }'"},{"lang":"typescript","label":"TypeScript (SDK)","source":"// npm install @revkeen/sdk@1.20260822.1541\nimport { RevKeenClient } from \"@revkeen/sdk\";\nconst apiKey = process.env.REVKEEN_API_KEY;\nif (!apiKey) throw new Error(\"Set REVKEEN_API_KEY\");\nconst client = new RevKeenClient({ apiKey, baseUrl: \"https://api.revkeen.com/v2\" });\nconst result = await client.paymentIntents.capture(\"pi_abc123\", {\n  \"amount_to_capture\": 1000\n});\nconsole.log(result);"},{"lang":"go","label":"Go (HTTP)","source":"package main\nimport (\"fmt\"; \"io\"; \"net/http\"; \"os\"; \"strings\"; \"time\")\nfunc main() {\n  req, err := http.NewRequest(\"POST\", \"https://api.revkeen.com/v2/payment-intents/pi_abc123/capture\", strings.NewReader(\"{\\\"amount_to_capture\\\":1000}\"))\n  if err != nil { panic(err) }\n  req.Header.Set(\"x-api-key\", os.Getenv(\"REVKEEN_API_KEY\"))\n  req.Header.Set(\"Content-Type\", \"application/json\")\n  client := &http.Client{Timeout: 10 * time.Second}\n  response, err := client.Do(req)\n  if err != nil { panic(err) }\n  defer response.Body.Close()\n  if response.StatusCode >= 400 { panic(fmt.Sprintf(\"RevKeen request failed (%d)\", response.StatusCode)) }\n  data, err := io.ReadAll(response.Body)\n  if err != nil { panic(err) }\n  fmt.Println(string(data))\n}"},{"lang":"php","label":"PHP (HTTP)","source":"<?php\n$request = curl_init(\"https://api.revkeen.com/v2/payment-intents/pi_abc123/capture\");\ncurl_setopt_array($request, [\n  CURLOPT_CUSTOMREQUEST => \"POST\",\n  CURLOPT_RETURNTRANSFER => true,\n  CURLOPT_TIMEOUT => 10,\n  CURLOPT_HTTPHEADER => [\"x-api-key: \" . getenv(\"REVKEEN_API_KEY\"), \"Content-Type: application/json\"],\n  CURLOPT_POSTFIELDS => '{\\\"amount_to_capture\\\":1000}',\n]);\n$response = curl_exec($request);\nif ($response === false) throw new RuntimeException(curl_error($request));\n$status = curl_getinfo($request, CURLINFO_HTTP_CODE);\ncurl_close($request);\nif ($status >= 400) throw new RuntimeException(\"RevKeen request failed ($status)\");\necho $response;"},{"lang":"python","label":"Python","source":"import os\n\nimport requests\n\nresponse = requests.post(\n    \"https://api.revkeen.com/v2/payment-intents/pi_abc123/capture\",\n    headers={\"x-api-key\": os.environ[\"REVKEEN_API_KEY\"]},\n    json={\n        \"amount_to_capture\": 1000,\n    },\n)\n\nresponse.raise_for_status()\nprint(response.json())"},{"lang":"javascript","label":"JavaScript","source":"const credential = process.env.REVKEEN_API_KEY;\nif (!credential) throw new Error(\"Set REVKEEN_API_KEY\");\nconst response = await fetch(\"https://api.revkeen.com/v2/payment-intents/pi_abc123/capture\", {\n  method: \"POST\",\n  headers: {\n    \"x-api-key\": credential,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n    \"amount_to_capture\": 1000\n  }),\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"java","label":"Java","source":"import java.net.URI;\nimport java.net.http.HttpClient;\nimport java.net.http.HttpRequest;\nimport java.net.http.HttpResponse;\n\nHttpClient client = HttpClient.newHttpClient();\n\nHttpRequest request = HttpRequest.newBuilder()\n    .uri(URI.create(\"https://api.revkeen.com/v2/payment-intents/pi_abc123/capture\"))\n    .header(\"x-api-key\", System.getenv(\"REVKEEN_API_KEY\"))\n    .header(\"Content-Type\", \"application/json\")\n    .method(\"POST\", HttpRequest.BodyPublishers.ofString(\"\"\"\n        {\n          \"amount_to_capture\": 1000\n        }\n        \"\"\"))\n    .build();\n\nHttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());\nSystem.out.println(response.body());"},{"lang":"ruby","label":"Ruby","source":"require \"net/http\"\nrequire \"json\"\nrequire \"uri\"\n\nuri = URI(\"https://api.revkeen.com/v2/payment-intents/pi_abc123/capture\")\nhttp = Net::HTTP.new(uri.host, uri.port)\nhttp.use_ssl = true\n\nrequest = Net::HTTP::Post.new(uri)\nrequest[\"x-api-key\"] = ENV[\"REVKEEN_API_KEY\"]\nrequest[\"Content-Type\"] = \"application/json\"\nrequest.body = {\n  \"amount_to_capture\" => 1000,\n}.to_json\n\nresponse = http.request(request)\nputs response.body"}],"summary":"Capture a payment intent","description":"Captures a payment intent that was created with capture_method=manual and is `processing`. The card was charged when the intent was confirmed (the gateway takes a sale, not an authorization), so capture does not call the gateway again: it marks the intent `succeeded` and records the sale transaction, returned as `transaction`. `POST /v2/payment-intents` refuses `capture_method=manual` for now (400 `capture_method_unsupported`), so an intent created through this API is never capturable.\n\n---\n\n**Related endpoints**\n\n- `POST /payment-intents` — Create a payment intent\n- `GET /payment-intents` — List payment intents\n- `GET /payment-intents/{id}` — Retrieve a payment intent\n- `POST /payment-intents/{id}/confirm` — Confirm a payment intent\n- `POST /payment-intents/{id}/cancel` — Cancel a payment intent\n\n**Common errors**\n\n- `400 invalid_request` — malformed payload or failed validation.\n- `404 resource_missing` — the referenced resource does not exist or is not visible to your key.\n\n**Idempotency**\n\nPass an `Idempotency-Key` header (UUID v4 recommended) to make retries safe. Keys are valid for 24 hours; see [the idempotency guide](/docs/fundamentals/idempotency).","parameters":[{"schema":{"type":"string","pattern":"^(?:pi_[A-Za-z0-9]+|[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12})$","description":"Payment intent public ID (`pi_…`) or UUID.","example":"pi_abc123"},"required":true,"description":"Payment intent public ID (`pi_…`) or UUID.","name":"id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CapturePaymentIntentRequest"}}}},"responses":{"200":{"description":"Captured payment intent","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaymentIntent"}}}},"400":{"description":"Invalid request or capture failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaymentIntentErrorResponse"}}}},"401":{"description":"Unauthorized - Invalid or missing API key","headers":{"WWW-Authenticate":{"description":"RFC 6750 Bearer authentication challenge","schema":{"type":"string"},"example":"Bearer realm=\"revkeen-api\""}}},"404":{"description":"Payment intent not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaymentIntentErrorResponse"}}}},"429":{"description":"Too Many Requests - Rate limit exceeded","headers":{"Retry-After":{"description":"Whole seconds until the caller may retry","schema":{"type":"string"},"example":"60"},"X-RateLimit-Limit":{"description":"Request quota for the current window","schema":{"type":"string"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window","schema":{"type":"string"}},"X-RateLimit-Reset":{"description":"UNIX timestamp in seconds when the window resets","schema":{"type":"string"}}}}},"x-cli-group":"payment-intents","x-cli-name":"capture","security":[{"apiKey":["payment_intents:write"]},{"oauth2":["payment_intents:write"]}]}},"/payment-intents/{id}/cancel":{"post":{"operationId":"payment_intents_cancel","tags":["Payment Intents"],"x-fern-sdk-group-name":["payment_intents"],"x-codeSamples":[{"lang":"curl","label":"cURL","source":"curl -X POST \"https://api.revkeen.com/v2/payment-intents/pi_abc123/cancel\" \\\n  -H \"x-api-key: $REVKEEN_API_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"cancellation_reason\": \"duplicate\"\n  }'"},{"lang":"typescript","label":"TypeScript (SDK)","source":"// npm install @revkeen/sdk@1.20260822.1541\nimport { RevKeenClient } from \"@revkeen/sdk\";\nconst apiKey = process.env.REVKEEN_API_KEY;\nif (!apiKey) throw new Error(\"Set REVKEEN_API_KEY\");\nconst client = new RevKeenClient({ apiKey, baseUrl: \"https://api.revkeen.com/v2\" });\nconst result = await client.paymentIntents.cancel(\"pi_abc123\", {\n  \"cancellation_reason\": \"duplicate\"\n});\nconsole.log(result);"},{"lang":"go","label":"Go (HTTP)","source":"package main\nimport (\"fmt\"; \"io\"; \"net/http\"; \"os\"; \"strings\"; \"time\")\nfunc main() {\n  req, err := http.NewRequest(\"POST\", \"https://api.revkeen.com/v2/payment-intents/pi_abc123/cancel\", strings.NewReader(\"{\\\"cancellation_reason\\\":\\\"duplicate\\\"}\"))\n  if err != nil { panic(err) }\n  req.Header.Set(\"x-api-key\", os.Getenv(\"REVKEEN_API_KEY\"))\n  req.Header.Set(\"Content-Type\", \"application/json\")\n  client := &http.Client{Timeout: 10 * time.Second}\n  response, err := client.Do(req)\n  if err != nil { panic(err) }\n  defer response.Body.Close()\n  if response.StatusCode >= 400 { panic(fmt.Sprintf(\"RevKeen request failed (%d)\", response.StatusCode)) }\n  data, err := io.ReadAll(response.Body)\n  if err != nil { panic(err) }\n  fmt.Println(string(data))\n}"},{"lang":"php","label":"PHP (HTTP)","source":"<?php\n$request = curl_init(\"https://api.revkeen.com/v2/payment-intents/pi_abc123/cancel\");\ncurl_setopt_array($request, [\n  CURLOPT_CUSTOMREQUEST => \"POST\",\n  CURLOPT_RETURNTRANSFER => true,\n  CURLOPT_TIMEOUT => 10,\n  CURLOPT_HTTPHEADER => [\"x-api-key: \" . getenv(\"REVKEEN_API_KEY\"), \"Content-Type: application/json\"],\n  CURLOPT_POSTFIELDS => '{\\\"cancellation_reason\\\":\\\"duplicate\\\"}',\n]);\n$response = curl_exec($request);\nif ($response === false) throw new RuntimeException(curl_error($request));\n$status = curl_getinfo($request, CURLINFO_HTTP_CODE);\ncurl_close($request);\nif ($status >= 400) throw new RuntimeException(\"RevKeen request failed ($status)\");\necho $response;"},{"lang":"python","label":"Python","source":"import os\n\nimport requests\n\nresponse = requests.post(\n    \"https://api.revkeen.com/v2/payment-intents/pi_abc123/cancel\",\n    headers={\"x-api-key\": os.environ[\"REVKEEN_API_KEY\"]},\n    json={\n        \"cancellation_reason\": \"duplicate\",\n    },\n)\n\nresponse.raise_for_status()\nprint(response.json())"},{"lang":"javascript","label":"JavaScript","source":"const credential = process.env.REVKEEN_API_KEY;\nif (!credential) throw new Error(\"Set REVKEEN_API_KEY\");\nconst response = await fetch(\"https://api.revkeen.com/v2/payment-intents/pi_abc123/cancel\", {\n  method: \"POST\",\n  headers: {\n    \"x-api-key\": credential,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n    \"cancellation_reason\": \"duplicate\"\n  }),\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"java","label":"Java","source":"import java.net.URI;\nimport java.net.http.HttpClient;\nimport java.net.http.HttpRequest;\nimport java.net.http.HttpResponse;\n\nHttpClient client = HttpClient.newHttpClient();\n\nHttpRequest request = HttpRequest.newBuilder()\n    .uri(URI.create(\"https://api.revkeen.com/v2/payment-intents/pi_abc123/cancel\"))\n    .header(\"x-api-key\", System.getenv(\"REVKEEN_API_KEY\"))\n    .header(\"Content-Type\", \"application/json\")\n    .method(\"POST\", HttpRequest.BodyPublishers.ofString(\"\"\"\n        {\n          \"cancellation_reason\": \"duplicate\"\n        }\n        \"\"\"))\n    .build();\n\nHttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());\nSystem.out.println(response.body());"},{"lang":"ruby","label":"Ruby","source":"require \"net/http\"\nrequire \"json\"\nrequire \"uri\"\n\nuri = URI(\"https://api.revkeen.com/v2/payment-intents/pi_abc123/cancel\")\nhttp = Net::HTTP.new(uri.host, uri.port)\nhttp.use_ssl = true\n\nrequest = Net::HTTP::Post.new(uri)\nrequest[\"x-api-key\"] = ENV[\"REVKEEN_API_KEY\"]\nrequest[\"Content-Type\"] = \"application/json\"\nrequest.body = {\n  \"cancellation_reason\" => \"duplicate\",\n}.to_json\n\nresponse = http.request(request)\nputs response.body"}],"summary":"Cancel a payment intent","description":"Cancels a payment intent. Cannot cancel if already succeeded or canceled. A `processing` intent that has already been charged at the gateway cannot be canceled either (409 `payment_intent_charged`): canceling would not return the money.\n\n---\n\n**Related endpoints**\n\n- `POST /payment-intents` — Create a payment intent\n- `GET /payment-intents` — List payment intents\n- `GET /payment-intents/{id}` — Retrieve a payment intent\n- `POST /payment-intents/{id}/confirm` — Confirm a payment intent\n- `POST /payment-intents/{id}/capture` — Capture a payment intent\n\n**Common errors**\n\n- `400 invalid_request` — malformed payload or failed validation.\n- `404 resource_missing` — the referenced resource does not exist or is not visible to your key.\n- `409 conflict` — Idempotency-Key collision with a different body, or a concurrent state-transition conflict.\n\n**Idempotency**\n\nPass an `Idempotency-Key` header (UUID v4 recommended) to make retries safe. Keys are valid for 24 hours; see [the idempotency guide](/docs/fundamentals/idempotency).","parameters":[{"schema":{"type":"string","pattern":"^(?:pi_[A-Za-z0-9]+|[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12})$","description":"Payment intent public ID (`pi_…`) or UUID.","example":"pi_abc123"},"required":true,"description":"Payment intent public ID (`pi_…`) or UUID.","name":"id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CancelPaymentIntentRequest"}}}},"responses":{"200":{"description":"Canceled payment intent","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaymentIntent"}}}},"400":{"description":"Invalid request or cancellation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaymentIntentErrorResponse"}}}},"401":{"description":"Unauthorized - Invalid or missing API key","headers":{"WWW-Authenticate":{"description":"RFC 6750 Bearer authentication challenge","schema":{"type":"string"},"example":"Bearer realm=\"revkeen-api\""}}},"404":{"description":"Payment intent not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaymentIntentErrorResponse"}}}},"409":{"description":"`payment_intent_charged`: the intent has already been charged at the gateway and cannot be canceled. Nothing changed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaymentIntentErrorResponse"}}}},"429":{"description":"Too Many Requests - Rate limit exceeded","headers":{"Retry-After":{"description":"Whole seconds until the caller may retry","schema":{"type":"string"},"example":"60"},"X-RateLimit-Limit":{"description":"Request quota for the current window","schema":{"type":"string"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window","schema":{"type":"string"}},"X-RateLimit-Reset":{"description":"UNIX timestamp in seconds when the window resets","schema":{"type":"string"}}}}},"x-cli-group":"payment-intents","x-cli-name":"cancel","security":[{"apiKey":["payment_intents:write"]},{"oauth2":["payment_intents:write"]}]}},"/payment-methods/{id}/detach":{"post":{"operationId":"payment_methods_detach","tags":["Payment Methods"],"summary":"Detach a payment method from its customer","description":"Detaches a payment method from its customer. After detaching, the payment method can no longer be used for payments.\n\nThis operation soft-deletes the payment method by setting its status to `inactive`; nothing is deleted from the card vault.\n\n**Refused with `409 payment_method_in_use`** (and nothing is changed) when the payment method is:\n\n- the customer's default payment method (set another default first), or\n- the payment method of a subscription that is `active`, `trialing` or `past_due` (move the subscription to another payment method first).\n\nDetaching a payment method that is already inactive returns it unchanged.\n\nRequires the `payment_methods:write` scope and the `payment_method:detach` permission. Send an `Idempotency-Key` header to make retries safe.\n\n---\n\n**Related endpoints**\n\n- `POST /payment-methods/{id}/set-default` — Set a payment method as its customer's default\n\n**Common errors**\n\n- `401 authentication_error` — missing, invalid, expired, or revoked credential. Codes: `authentication_failed`, `invalid_api_key`, `expired_api_key`, `api_key_revoked`, `session_invalid`, `merchant_required`. Carries a `WWW-Authenticate: Bearer` challenge.\n- `403 authorization_error` — the caller is identified but the action is denied. Codes include `insufficient_permissions`, `ip_not_allowed`, `merchant_mismatch`, and `origin_not_allowed`.\n- `404 resource_missing` — the referenced resource does not exist or is not visible to your key.\n- `409 conflict` — Idempotency-Key collision with a different body, or a concurrent state-transition conflict.\n- `429 rate_limit_error` — code `rate_limit_exceeded`. Back off using the `Retry-After` header (whole seconds); `X-RateLimit-Reset` is a UNIX timestamp in seconds.\n\n**Idempotency**\n\nPass an `Idempotency-Key` header (UUID v4 recommended) to make retries safe. Keys are valid for 24 hours; see [the idempotency guide](/docs/fundamentals/idempotency).","security":[{"apiKey":["payment_methods:write"]}],"x-fern-sdk-group-name":["payment_methods"],"x-fern-sdk-method-name":"detach","parameters":[{"schema":{"type":"string","description":"Payment method UUID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Payment method UUID","name":"id","in":"path"}],"responses":{"200":{"description":"Payment method detached","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaymentMethod"}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthenticationError"}}},"headers":{"WWW-Authenticate":{"description":"RFC 6750 Bearer authentication challenge","schema":{"type":"string"},"example":"Bearer realm=\"revkeen-api\""}}},"403":{"description":"Insufficient scope (`payment_methods:write`), or the authorization policy denied this action for the caller","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MerchantActionForbiddenError"}}}},"404":{"description":"Payment method not found (or not yours)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotFoundError"}}}},"409":{"description":"The payment method is in use and cannot be detached","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaymentMethodConflictError"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RateLimitError"}}},"headers":{"Retry-After":{"description":"Whole seconds until the caller may retry","schema":{"type":"string"},"example":"60"},"X-RateLimit-Limit":{"description":"Request quota for the current window","schema":{"type":"string"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window","schema":{"type":"string"}},"X-RateLimit-Reset":{"description":"UNIX timestamp in seconds when the window resets","schema":{"type":"string"}}}},"503":{"description":"A required audit record could not be written; the action was not performed. Retry later.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthorizationAuditUnavailableError"}}}}},"x-cli-group":"payment-methods","x-cli-name":"detach","x-codeSamples":[{"lang":"curl","label":"cURL","source":"curl -X POST \"https://api.revkeen.com/v2/payment-methods/550e8400-e29b-41d4-a716-446655440000/detach\" \\\n  -H \"x-api-key: $REVKEEN_API_KEY\""},{"lang":"typescript","label":"TypeScript (HTTP)","source":"const credential = process.env.REVKEEN_API_KEY;\nif (!credential) throw new Error(\"Set REVKEEN_API_KEY\");\nconst response = await fetch(\"https://api.revkeen.com/v2/payment-methods/550e8400-e29b-41d4-a716-446655440000/detach\", {\n  method: \"POST\",\n  headers: {\n    \"x-api-key\": credential,\n  },\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"go","label":"Go (HTTP)","source":"package main\nimport (\"fmt\"; \"io\"; \"net/http\"; \"os\"; \"strings\"; \"time\")\nfunc main() {\n  req, err := http.NewRequest(\"POST\", \"https://api.revkeen.com/v2/payment-methods/550e8400-e29b-41d4-a716-446655440000/detach\", strings.NewReader(\"\"))\n  if err != nil { panic(err) }\n  req.Header.Set(\"x-api-key\", os.Getenv(\"REVKEEN_API_KEY\"))\n  req.Header.Set(\"Content-Type\", \"application/json\")\n  client := &http.Client{Timeout: 10 * time.Second}\n  response, err := client.Do(req)\n  if err != nil { panic(err) }\n  defer response.Body.Close()\n  if response.StatusCode >= 400 { panic(fmt.Sprintf(\"RevKeen request failed (%d)\", response.StatusCode)) }\n  data, err := io.ReadAll(response.Body)\n  if err != nil { panic(err) }\n  fmt.Println(string(data))\n}"},{"lang":"php","label":"PHP (HTTP)","source":"<?php\n$request = curl_init(\"https://api.revkeen.com/v2/payment-methods/550e8400-e29b-41d4-a716-446655440000/detach\");\ncurl_setopt_array($request, [\n  CURLOPT_CUSTOMREQUEST => \"POST\",\n  CURLOPT_RETURNTRANSFER => true,\n  CURLOPT_TIMEOUT => 10,\n  CURLOPT_HTTPHEADER => [\"x-api-key: \" . getenv(\"REVKEEN_API_KEY\"), \"Content-Type: application/json\"],\n]);\n$response = curl_exec($request);\nif ($response === false) throw new RuntimeException(curl_error($request));\n$status = curl_getinfo($request, CURLINFO_HTTP_CODE);\ncurl_close($request);\nif ($status >= 400) throw new RuntimeException(\"RevKeen request failed ($status)\");\necho $response;"},{"lang":"python","label":"Python","source":"import os\n\nimport requests\n\nresponse = requests.post(\n    \"https://api.revkeen.com/v2/payment-methods/550e8400-e29b-41d4-a716-446655440000/detach\",\n    headers={\"x-api-key\": os.environ[\"REVKEEN_API_KEY\"]},\n)\n\nresponse.raise_for_status()\nprint(response.json())"},{"lang":"javascript","label":"JavaScript","source":"const credential = process.env.REVKEEN_API_KEY;\nif (!credential) throw new Error(\"Set REVKEEN_API_KEY\");\nconst response = await fetch(\"https://api.revkeen.com/v2/payment-methods/550e8400-e29b-41d4-a716-446655440000/detach\", {\n  method: \"POST\",\n  headers: {\n    \"x-api-key\": credential,\n  },\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"java","label":"Java","source":"import java.net.URI;\nimport java.net.http.HttpClient;\nimport java.net.http.HttpRequest;\nimport java.net.http.HttpResponse;\n\nHttpClient client = HttpClient.newHttpClient();\n\nHttpRequest request = HttpRequest.newBuilder()\n    .uri(URI.create(\"https://api.revkeen.com/v2/payment-methods/550e8400-e29b-41d4-a716-446655440000/detach\"))\n    .header(\"x-api-key\", System.getenv(\"REVKEEN_API_KEY\"))\n    .method(\"POST\", HttpRequest.BodyPublishers.noBody())\n    .build();\n\nHttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());\nSystem.out.println(response.body());"},{"lang":"ruby","label":"Ruby","source":"require \"net/http\"\nrequire \"json\"\nrequire \"uri\"\n\nuri = URI(\"https://api.revkeen.com/v2/payment-methods/550e8400-e29b-41d4-a716-446655440000/detach\")\nhttp = Net::HTTP.new(uri.host, uri.port)\nhttp.use_ssl = true\n\nrequest = Net::HTTP::Post.new(uri)\nrequest[\"x-api-key\"] = ENV[\"REVKEEN_API_KEY\"]\n\nresponse = http.request(request)\nputs response.body"}]}},"/payment-methods/{id}/set-default":{"post":{"operationId":"payment_methods_set_default","tags":["Payment Methods"],"summary":"Set a payment method as its customer's default","description":"Makes this payment method the default payment method of the customer it belongs to. The previous default, if any, stops being the default.\n\nThis sets the **customer's default only**. It does not move any existing subscription to this payment method: a subscription keeps the payment method it already bills on.\n\nA payment method that is not active (detached or expired) is refused with `409 payment_method_not_active` and nothing is changed. Setting a payment method that is already the default is a no-op that returns it.\n\nRequires the `payment_methods:write` scope and the `payment_method:set_default` permission. Send an `Idempotency-Key` header to make retries safe.\n\n---\n\n**Related endpoints**\n\n- `POST /payment-methods/{id}/detach` — Detach a payment method from its customer\n\n**Common errors**\n\n- `401 authentication_error` — missing, invalid, expired, or revoked credential. Codes: `authentication_failed`, `invalid_api_key`, `expired_api_key`, `api_key_revoked`, `session_invalid`, `merchant_required`. Carries a `WWW-Authenticate: Bearer` challenge.\n- `403 authorization_error` — the caller is identified but the action is denied. Codes include `insufficient_permissions`, `ip_not_allowed`, `merchant_mismatch`, and `origin_not_allowed`.\n- `404 resource_missing` — the referenced resource does not exist or is not visible to your key.\n- `409 conflict` — Idempotency-Key collision with a different body, or a concurrent state-transition conflict.\n- `429 rate_limit_error` — code `rate_limit_exceeded`. Back off using the `Retry-After` header (whole seconds); `X-RateLimit-Reset` is a UNIX timestamp in seconds.\n\n**Idempotency**\n\nPass an `Idempotency-Key` header (UUID v4 recommended) to make retries safe. Keys are valid for 24 hours; see [the idempotency guide](/docs/fundamentals/idempotency).","security":[{"apiKey":["payment_methods:write"]}],"x-fern-sdk-group-name":["payment_methods"],"x-fern-sdk-method-name":"setDefault","parameters":[{"schema":{"type":"string","description":"Payment method UUID","example":"550e8400-e29b-41d4-a716-446655440000"},"required":true,"description":"Payment method UUID","name":"id","in":"path"}],"responses":{"200":{"description":"Payment method is now the customer's default","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaymentMethod"}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthenticationError"}}},"headers":{"WWW-Authenticate":{"description":"RFC 6750 Bearer authentication challenge","schema":{"type":"string"},"example":"Bearer realm=\"revkeen-api\""}}},"403":{"description":"Insufficient scope (`payment_methods:write`), or the authorization policy denied this action for the caller","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MerchantActionForbiddenError"}}}},"404":{"description":"Payment method not found (or not yours)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotFoundError"}}}},"409":{"description":"The payment method is not active and cannot be the default","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaymentMethodConflictError"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RateLimitError"}}},"headers":{"Retry-After":{"description":"Whole seconds until the caller may retry","schema":{"type":"string"},"example":"60"},"X-RateLimit-Limit":{"description":"Request quota for the current window","schema":{"type":"string"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window","schema":{"type":"string"}},"X-RateLimit-Reset":{"description":"UNIX timestamp in seconds when the window resets","schema":{"type":"string"}}}},"503":{"description":"A required audit record could not be written; the action was not performed. Retry later.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthorizationAuditUnavailableError"}}}}},"x-cli-group":"payment-methods","x-cli-name":"set-default","x-codeSamples":[{"lang":"curl","label":"cURL","source":"curl -X POST \"https://api.revkeen.com/v2/payment-methods/550e8400-e29b-41d4-a716-446655440000/set-default\" \\\n  -H \"x-api-key: $REVKEEN_API_KEY\""},{"lang":"typescript","label":"TypeScript (HTTP)","source":"const credential = process.env.REVKEEN_API_KEY;\nif (!credential) throw new Error(\"Set REVKEEN_API_KEY\");\nconst response = await fetch(\"https://api.revkeen.com/v2/payment-methods/550e8400-e29b-41d4-a716-446655440000/set-default\", {\n  method: \"POST\",\n  headers: {\n    \"x-api-key\": credential,\n  },\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"go","label":"Go (HTTP)","source":"package main\nimport (\"fmt\"; \"io\"; \"net/http\"; \"os\"; \"strings\"; \"time\")\nfunc main() {\n  req, err := http.NewRequest(\"POST\", \"https://api.revkeen.com/v2/payment-methods/550e8400-e29b-41d4-a716-446655440000/set-default\", strings.NewReader(\"\"))\n  if err != nil { panic(err) }\n  req.Header.Set(\"x-api-key\", os.Getenv(\"REVKEEN_API_KEY\"))\n  req.Header.Set(\"Content-Type\", \"application/json\")\n  client := &http.Client{Timeout: 10 * time.Second}\n  response, err := client.Do(req)\n  if err != nil { panic(err) }\n  defer response.Body.Close()\n  if response.StatusCode >= 400 { panic(fmt.Sprintf(\"RevKeen request failed (%d)\", response.StatusCode)) }\n  data, err := io.ReadAll(response.Body)\n  if err != nil { panic(err) }\n  fmt.Println(string(data))\n}"},{"lang":"php","label":"PHP (HTTP)","source":"<?php\n$request = curl_init(\"https://api.revkeen.com/v2/payment-methods/550e8400-e29b-41d4-a716-446655440000/set-default\");\ncurl_setopt_array($request, [\n  CURLOPT_CUSTOMREQUEST => \"POST\",\n  CURLOPT_RETURNTRANSFER => true,\n  CURLOPT_TIMEOUT => 10,\n  CURLOPT_HTTPHEADER => [\"x-api-key: \" . getenv(\"REVKEEN_API_KEY\"), \"Content-Type: application/json\"],\n]);\n$response = curl_exec($request);\nif ($response === false) throw new RuntimeException(curl_error($request));\n$status = curl_getinfo($request, CURLINFO_HTTP_CODE);\ncurl_close($request);\nif ($status >= 400) throw new RuntimeException(\"RevKeen request failed ($status)\");\necho $response;"},{"lang":"python","label":"Python","source":"import os\n\nimport requests\n\nresponse = requests.post(\n    \"https://api.revkeen.com/v2/payment-methods/550e8400-e29b-41d4-a716-446655440000/set-default\",\n    headers={\"x-api-key\": os.environ[\"REVKEEN_API_KEY\"]},\n)\n\nresponse.raise_for_status()\nprint(response.json())"},{"lang":"javascript","label":"JavaScript","source":"const credential = process.env.REVKEEN_API_KEY;\nif (!credential) throw new Error(\"Set REVKEEN_API_KEY\");\nconst response = await fetch(\"https://api.revkeen.com/v2/payment-methods/550e8400-e29b-41d4-a716-446655440000/set-default\", {\n  method: \"POST\",\n  headers: {\n    \"x-api-key\": credential,\n  },\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"java","label":"Java","source":"import java.net.URI;\nimport java.net.http.HttpClient;\nimport java.net.http.HttpRequest;\nimport java.net.http.HttpResponse;\n\nHttpClient client = HttpClient.newHttpClient();\n\nHttpRequest request = HttpRequest.newBuilder()\n    .uri(URI.create(\"https://api.revkeen.com/v2/payment-methods/550e8400-e29b-41d4-a716-446655440000/set-default\"))\n    .header(\"x-api-key\", System.getenv(\"REVKEEN_API_KEY\"))\n    .method(\"POST\", HttpRequest.BodyPublishers.noBody())\n    .build();\n\nHttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());\nSystem.out.println(response.body());"},{"lang":"ruby","label":"Ruby","source":"require \"net/http\"\nrequire \"json\"\nrequire \"uri\"\n\nuri = URI(\"https://api.revkeen.com/v2/payment-methods/550e8400-e29b-41d4-a716-446655440000/set-default\")\nhttp = Net::HTTP.new(uri.host, uri.port)\nhttp.use_ssl = true\n\nrequest = Net::HTTP::Post.new(uri)\nrequest[\"x-api-key\"] = ENV[\"REVKEEN_API_KEY\"]\n\nresponse = http.request(request)\nputs response.body"}]}},"/events":{"get":{"operationId":"events_list","tags":["Events"],"x-fern-sdk-group-name":["events"],"x-codeSamples":[{"lang":"curl","label":"cURL","source":"curl -X GET \"https://api.revkeen.com/v2/events?type=invoice.paid&types=invoice.paid%2Cpayment.succeeded\" \\\n  -H \"x-api-key: $REVKEEN_API_KEY\""},{"lang":"typescript","label":"TypeScript (SDK)","source":"// npm install @revkeen/sdk@1.20260822.1541\nimport { RevKeenClient } from \"@revkeen/sdk\";\nconst apiKey = process.env.REVKEEN_API_KEY;\nif (!apiKey) throw new Error(\"Set REVKEEN_API_KEY\");\nconst client = new RevKeenClient({ apiKey, baseUrl: \"https://api.revkeen.com/v2\" });\nconst result = await client.events.list({\n  \"type\": \"invoice.paid\",\n  \"types\": \"invoice.paid,payment.succeeded\"\n});\nconsole.log(result);"},{"lang":"go","label":"Go (HTTP)","source":"package main\nimport (\"fmt\"; \"io\"; \"net/http\"; \"os\"; \"strings\"; \"time\")\nfunc main() {\n  req, err := http.NewRequest(\"GET\", \"https://api.revkeen.com/v2/events?type=invoice.paid&types=invoice.paid%2Cpayment.succeeded\", strings.NewReader(\"\"))\n  if err != nil { panic(err) }\n  req.Header.Set(\"x-api-key\", os.Getenv(\"REVKEEN_API_KEY\"))\n  req.Header.Set(\"Content-Type\", \"application/json\")\n  client := &http.Client{Timeout: 10 * time.Second}\n  response, err := client.Do(req)\n  if err != nil { panic(err) }\n  defer response.Body.Close()\n  if response.StatusCode >= 400 { panic(fmt.Sprintf(\"RevKeen request failed (%d)\", response.StatusCode)) }\n  data, err := io.ReadAll(response.Body)\n  if err != nil { panic(err) }\n  fmt.Println(string(data))\n}"},{"lang":"php","label":"PHP (HTTP)","source":"<?php\n$request = curl_init(\"https://api.revkeen.com/v2/events?type=invoice.paid&types=invoice.paid%2Cpayment.succeeded\");\ncurl_setopt_array($request, [\n  CURLOPT_CUSTOMREQUEST => \"GET\",\n  CURLOPT_RETURNTRANSFER => true,\n  CURLOPT_TIMEOUT => 10,\n  CURLOPT_HTTPHEADER => [\"x-api-key: \" . getenv(\"REVKEEN_API_KEY\"), \"Content-Type: application/json\"],\n]);\n$response = curl_exec($request);\nif ($response === false) throw new RuntimeException(curl_error($request));\n$status = curl_getinfo($request, CURLINFO_HTTP_CODE);\ncurl_close($request);\nif ($status >= 400) throw new RuntimeException(\"RevKeen request failed ($status)\");\necho $response;"},{"lang":"python","label":"Python","source":"import os\n\nimport requests\n\nresponse = requests.get(\n    \"https://api.revkeen.com/v2/events\",\n    headers={\"x-api-key\": os.environ[\"REVKEEN_API_KEY\"]},\n    params={\n        \"type\": \"invoice.paid\",\n        \"types\": \"invoice.paid,payment.succeeded\",\n    },\n)\n\nresponse.raise_for_status()\nprint(response.json())"},{"lang":"javascript","label":"JavaScript","source":"const credential = process.env.REVKEEN_API_KEY;\nif (!credential) throw new Error(\"Set REVKEEN_API_KEY\");\nconst response = await fetch(\"https://api.revkeen.com/v2/events?type=invoice.paid&types=invoice.paid%2Cpayment.succeeded\", {\n  method: \"GET\",\n  headers: {\n    \"x-api-key\": credential,\n  },\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"java","label":"Java","source":"import java.net.URI;\nimport java.net.http.HttpClient;\nimport java.net.http.HttpRequest;\nimport java.net.http.HttpResponse;\n\nHttpClient client = HttpClient.newHttpClient();\n\nHttpRequest request = HttpRequest.newBuilder()\n    .uri(URI.create(\"https://api.revkeen.com/v2/events?type=invoice.paid&types=invoice.paid%2Cpayment.succeeded\"))\n    .header(\"x-api-key\", System.getenv(\"REVKEEN_API_KEY\"))\n    .GET()\n    .build();\n\nHttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());\nSystem.out.println(response.body());"},{"lang":"ruby","label":"Ruby","source":"require \"net/http\"\nrequire \"json\"\nrequire \"uri\"\n\nuri = URI(\"https://api.revkeen.com/v2/events?type=invoice.paid&types=invoice.paid%2Cpayment.succeeded\")\nhttp = Net::HTTP.new(uri.host, uri.port)\nhttp.use_ssl = true\n\nrequest = Net::HTTP::Get.new(uri)\nrequest[\"x-api-key\"] = ENV[\"REVKEEN_API_KEY\"]\n\nresponse = http.request(request)\nputs response.body"}],"summary":"List events","description":"Returns a list of events with optional filters. Events are returned in reverse chronological order.\n\n---\n\n**Related endpoints**\n\n- `GET /events/{id}` — Retrieve an event\n- `POST /events/{id}/resend` — Resend webhook for an event\n\n**Common errors**\n\n- `401 authentication_error` — missing, invalid, expired, or revoked credential. Codes: `authentication_failed`, `invalid_api_key`, `expired_api_key`, `api_key_revoked`, `session_invalid`, `merchant_required`. Carries a `WWW-Authenticate: Bearer` challenge.\n\n**Pagination**\n\nOffset-based with `limit` (default 25, max 100) and `offset`. The response `pagination` block includes `total` and `hasMore`. See [the pagination guide](/docs/fundamentals/pagination) for SDK auto-paging helpers.","parameters":[{"schema":{"type":"string","description":"Filter by event type (e.g., invoice.paid)","example":"invoice.paid"},"required":false,"description":"Filter by event type (e.g., invoice.paid)","name":"type","in":"query"},{"schema":{"type":"string","description":"Filter by multiple event types (comma-separated)","example":"invoice.paid,payment.succeeded"},"required":false,"description":"Filter by multiple event types (comma-separated)","name":"types","in":"query"},{"schema":{"type":"string","format":"uuid","description":"Filter by customer ID"},"required":false,"description":"Filter by customer ID","name":"customer_id","in":"query"},{"schema":{"type":"string","format":"uuid","description":"Filter by invoice ID"},"required":false,"description":"Filter by invoice ID","name":"invoice_id","in":"query"},{"schema":{"type":"string","format":"uuid","description":"Filter by subscription ID"},"required":false,"description":"Filter by subscription ID","name":"subscription_id","in":"query"},{"schema":{"type":"string","format":"uuid","description":"Filter by order ID"},"required":false,"description":"Filter by order ID","name":"order_id","in":"query"},{"schema":{"type":["number","null"],"minimum":0,"maximum":253402300799,"description":"Filter by created_at >= (Unix timestamp)"},"required":false,"description":"Filter by created_at >= (Unix timestamp)","name":"created_gte","in":"query"},{"schema":{"type":["number","null"],"minimum":0,"maximum":253402300799,"description":"Filter by created_at <= (Unix timestamp)"},"required":false,"description":"Filter by created_at <= (Unix timestamp)","name":"created_lte","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":100,"default":20,"description":"Maximum number of results (1-100)"},"required":false,"description":"Maximum number of results (1-100)","name":"limit","in":"query"},{"schema":{"type":"string","format":"uuid","description":"Cursor for pagination - return results after this event ID"},"required":false,"description":"Cursor for pagination - return results after this event ID","name":"starting_after","in":"query"},{"schema":{"type":"string","format":"uuid","description":"Cursor for pagination - return results before this event ID"},"required":false,"description":"Cursor for pagination - return results before this event ID","name":"ending_before","in":"query"}],"responses":{"200":{"description":"List of events","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EventListResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EventErrorResponse"}}},"headers":{"WWW-Authenticate":{"description":"RFC 6750 Bearer authentication challenge","schema":{"type":"string"},"example":"Bearer realm=\"revkeen-api\""}}},"429":{"description":"Too Many Requests - Rate limit exceeded","headers":{"Retry-After":{"description":"Whole seconds until the caller may retry","schema":{"type":"string"},"example":"60"},"X-RateLimit-Limit":{"description":"Request quota for the current window","schema":{"type":"string"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window","schema":{"type":"string"}},"X-RateLimit-Reset":{"description":"UNIX timestamp in seconds when the window resets","schema":{"type":"string"}}}}},"x-cli-group":"events","x-cli-name":"list","security":[{"apiKey":["events:read"]},{"oauth2":["events:read"]}]}},"/events/{id}":{"get":{"operationId":"events_get","tags":["Events"],"x-fern-sdk-group-name":["events"],"x-codeSamples":[{"lang":"curl","label":"cURL","source":"curl -X GET \"https://api.revkeen.com/v2/events/11111111-1111-4111-8111-111111111111\" \\\n  -H \"x-api-key: $REVKEEN_API_KEY\""},{"lang":"typescript","label":"TypeScript (SDK)","source":"// npm install @revkeen/sdk@1.20260822.1541\nimport { RevKeenClient } from \"@revkeen/sdk\";\nconst apiKey = process.env.REVKEEN_API_KEY;\nif (!apiKey) throw new Error(\"Set REVKEEN_API_KEY\");\nconst client = new RevKeenClient({ apiKey, baseUrl: \"https://api.revkeen.com/v2\" });\nconst result = await client.events.get(\"11111111-1111-4111-8111-111111111111\");\nconsole.log(result);"},{"lang":"go","label":"Go (HTTP)","source":"package main\nimport (\"fmt\"; \"io\"; \"net/http\"; \"os\"; \"strings\"; \"time\")\nfunc main() {\n  req, err := http.NewRequest(\"GET\", \"https://api.revkeen.com/v2/events/11111111-1111-4111-8111-111111111111\", strings.NewReader(\"\"))\n  if err != nil { panic(err) }\n  req.Header.Set(\"x-api-key\", os.Getenv(\"REVKEEN_API_KEY\"))\n  req.Header.Set(\"Content-Type\", \"application/json\")\n  client := &http.Client{Timeout: 10 * time.Second}\n  response, err := client.Do(req)\n  if err != nil { panic(err) }\n  defer response.Body.Close()\n  if response.StatusCode >= 400 { panic(fmt.Sprintf(\"RevKeen request failed (%d)\", response.StatusCode)) }\n  data, err := io.ReadAll(response.Body)\n  if err != nil { panic(err) }\n  fmt.Println(string(data))\n}"},{"lang":"php","label":"PHP (HTTP)","source":"<?php\n$request = curl_init(\"https://api.revkeen.com/v2/events/11111111-1111-4111-8111-111111111111\");\ncurl_setopt_array($request, [\n  CURLOPT_CUSTOMREQUEST => \"GET\",\n  CURLOPT_RETURNTRANSFER => true,\n  CURLOPT_TIMEOUT => 10,\n  CURLOPT_HTTPHEADER => [\"x-api-key: \" . getenv(\"REVKEEN_API_KEY\"), \"Content-Type: application/json\"],\n]);\n$response = curl_exec($request);\nif ($response === false) throw new RuntimeException(curl_error($request));\n$status = curl_getinfo($request, CURLINFO_HTTP_CODE);\ncurl_close($request);\nif ($status >= 400) throw new RuntimeException(\"RevKeen request failed ($status)\");\necho $response;"},{"lang":"python","label":"Python","source":"import os\n\nimport requests\n\nresponse = requests.get(\n    \"https://api.revkeen.com/v2/events/11111111-1111-4111-8111-111111111111\",\n    headers={\"x-api-key\": os.environ[\"REVKEEN_API_KEY\"]},\n)\n\nresponse.raise_for_status()\nprint(response.json())"},{"lang":"javascript","label":"JavaScript","source":"const credential = process.env.REVKEEN_API_KEY;\nif (!credential) throw new Error(\"Set REVKEEN_API_KEY\");\nconst response = await fetch(\"https://api.revkeen.com/v2/events/11111111-1111-4111-8111-111111111111\", {\n  method: \"GET\",\n  headers: {\n    \"x-api-key\": credential,\n  },\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"java","label":"Java","source":"import java.net.URI;\nimport java.net.http.HttpClient;\nimport java.net.http.HttpRequest;\nimport java.net.http.HttpResponse;\n\nHttpClient client = HttpClient.newHttpClient();\n\nHttpRequest request = HttpRequest.newBuilder()\n    .uri(URI.create(\"https://api.revkeen.com/v2/events/11111111-1111-4111-8111-111111111111\"))\n    .header(\"x-api-key\", System.getenv(\"REVKEEN_API_KEY\"))\n    .GET()\n    .build();\n\nHttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());\nSystem.out.println(response.body());"},{"lang":"ruby","label":"Ruby","source":"require \"net/http\"\nrequire \"json\"\nrequire \"uri\"\n\nuri = URI(\"https://api.revkeen.com/v2/events/11111111-1111-4111-8111-111111111111\")\nhttp = Net::HTTP.new(uri.host, uri.port)\nhttp.use_ssl = true\n\nrequest = Net::HTTP::Get.new(uri)\nrequest[\"x-api-key\"] = ENV[\"REVKEEN_API_KEY\"]\n\nresponse = http.request(request)\nputs response.body"}],"summary":"Retrieve an event","description":"Retrieves the details of an event by its ID.\n\n---\n\n**Related endpoints**\n\n- `GET /events` — List events\n- `POST /events/{id}/resend` — Resend webhook for an event\n\n**Common errors**\n\n- `401 authentication_error` — missing, invalid, expired, or revoked credential. Codes: `authentication_failed`, `invalid_api_key`, `expired_api_key`, `api_key_revoked`, `session_invalid`, `merchant_required`. Carries a `WWW-Authenticate: Bearer` challenge.\n- `404 resource_missing` — the referenced resource does not exist or is not visible to your key.","parameters":[{"schema":{"type":"string","format":"uuid","description":"Event ID"},"required":true,"description":"Event ID","name":"id","in":"path"}],"responses":{"200":{"description":"Event details","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Event"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EventErrorResponse"}}},"headers":{"WWW-Authenticate":{"description":"RFC 6750 Bearer authentication challenge","schema":{"type":"string"},"example":"Bearer realm=\"revkeen-api\""}}},"404":{"description":"Event not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EventErrorResponse"}}}},"429":{"description":"Too Many Requests - Rate limit exceeded","headers":{"Retry-After":{"description":"Whole seconds until the caller may retry","schema":{"type":"string"},"example":"60"},"X-RateLimit-Limit":{"description":"Request quota for the current window","schema":{"type":"string"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window","schema":{"type":"string"}},"X-RateLimit-Reset":{"description":"UNIX timestamp in seconds when the window resets","schema":{"type":"string"}}}}},"x-cli-group":"events","x-cli-name":"get","security":[{"apiKey":["events:read"]},{"oauth2":["events:read"]}]}},"/events/{id}/resend":{"post":{"operationId":"events_resend","tags":["Events"],"x-fern-sdk-group-name":["events"],"summary":"Resend webhook for an event","deprecated":true,"description":"Deprecated: use `POST /webhook-endpoints/{id}/redeliver`, which sends an event's original payload to one endpoint. This operation keeps working; no removal date has been set. Queues the event to be resent to all configured webhook endpoints. Responses carry a `Deprecation` header and a `Link` to the replacement.\n\n---\n\n**Related endpoints**\n\n- `GET /events` — List events\n- `GET /events/{id}` — Retrieve an event\n\n**Common errors**\n\n- `401 authentication_error` — missing, invalid, expired, or revoked credential. Codes: `authentication_failed`, `invalid_api_key`, `expired_api_key`, `api_key_revoked`, `session_invalid`, `merchant_required`. Carries a `WWW-Authenticate: Bearer` challenge.\n- `404 resource_missing` — the referenced resource does not exist or is not visible to your key.\n\n**Idempotency**\n\nPass an `Idempotency-Key` header (UUID v4 recommended) to make retries safe. Keys are valid for 24 hours; see [the idempotency guide](/docs/fundamentals/idempotency).","parameters":[{"schema":{"type":"string","format":"uuid","description":"Event ID"},"required":true,"description":"Event ID","name":"id","in":"path"}],"responses":{"200":{"description":"Webhook queued for resending","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResendWebhookResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EventErrorResponse"}}},"headers":{"WWW-Authenticate":{"description":"RFC 6750 Bearer authentication challenge","schema":{"type":"string"},"example":"Bearer realm=\"revkeen-api\""}}},"404":{"description":"Event not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EventErrorResponse"}}}},"429":{"description":"Too Many Requests - Rate limit exceeded","headers":{"Retry-After":{"description":"Whole seconds until the caller may retry","schema":{"type":"string"},"example":"60"},"X-RateLimit-Limit":{"description":"Request quota for the current window","schema":{"type":"string"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window","schema":{"type":"string"}},"X-RateLimit-Reset":{"description":"UNIX timestamp in seconds when the window resets","schema":{"type":"string"}}}}},"x-cli-group":"events","x-cli-name":"resend","x-codeSamples":[{"lang":"curl","label":"cURL","source":"curl -X POST \"https://api.revkeen.com/v2/events/11111111-1111-4111-8111-111111111111/resend\" \\\n  -H \"x-api-key: $REVKEEN_API_KEY\""},{"lang":"typescript","label":"TypeScript (SDK)","source":"// npm install @revkeen/sdk@1.20260822.1541\nimport { RevKeenClient } from \"@revkeen/sdk\";\nconst apiKey = process.env.REVKEEN_API_KEY;\nif (!apiKey) throw new Error(\"Set REVKEEN_API_KEY\");\nconst client = new RevKeenClient({ apiKey, baseUrl: \"https://api.revkeen.com/v2\" });\nconst result = await client.events.resend(\"11111111-1111-4111-8111-111111111111\");\nconsole.log(result);"},{"lang":"go","label":"Go (HTTP)","source":"package main\nimport (\"fmt\"; \"io\"; \"net/http\"; \"os\"; \"strings\"; \"time\")\nfunc main() {\n  req, err := http.NewRequest(\"POST\", \"https://api.revkeen.com/v2/events/11111111-1111-4111-8111-111111111111/resend\", strings.NewReader(\"\"))\n  if err != nil { panic(err) }\n  req.Header.Set(\"x-api-key\", os.Getenv(\"REVKEEN_API_KEY\"))\n  req.Header.Set(\"Content-Type\", \"application/json\")\n  client := &http.Client{Timeout: 10 * time.Second}\n  response, err := client.Do(req)\n  if err != nil { panic(err) }\n  defer response.Body.Close()\n  if response.StatusCode >= 400 { panic(fmt.Sprintf(\"RevKeen request failed (%d)\", response.StatusCode)) }\n  data, err := io.ReadAll(response.Body)\n  if err != nil { panic(err) }\n  fmt.Println(string(data))\n}"},{"lang":"php","label":"PHP (HTTP)","source":"<?php\n$request = curl_init(\"https://api.revkeen.com/v2/events/11111111-1111-4111-8111-111111111111/resend\");\ncurl_setopt_array($request, [\n  CURLOPT_CUSTOMREQUEST => \"POST\",\n  CURLOPT_RETURNTRANSFER => true,\n  CURLOPT_TIMEOUT => 10,\n  CURLOPT_HTTPHEADER => [\"x-api-key: \" . getenv(\"REVKEEN_API_KEY\"), \"Content-Type: application/json\"],\n]);\n$response = curl_exec($request);\nif ($response === false) throw new RuntimeException(curl_error($request));\n$status = curl_getinfo($request, CURLINFO_HTTP_CODE);\ncurl_close($request);\nif ($status >= 400) throw new RuntimeException(\"RevKeen request failed ($status)\");\necho $response;"},{"lang":"python","label":"Python","source":"import os\n\nimport requests\n\nresponse = requests.post(\n    \"https://api.revkeen.com/v2/events/11111111-1111-4111-8111-111111111111/resend\",\n    headers={\"x-api-key\": os.environ[\"REVKEEN_API_KEY\"]},\n)\n\nresponse.raise_for_status()\nprint(response.json())"},{"lang":"javascript","label":"JavaScript","source":"const credential = process.env.REVKEEN_API_KEY;\nif (!credential) throw new Error(\"Set REVKEEN_API_KEY\");\nconst response = await fetch(\"https://api.revkeen.com/v2/events/11111111-1111-4111-8111-111111111111/resend\", {\n  method: \"POST\",\n  headers: {\n    \"x-api-key\": credential,\n  },\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"java","label":"Java","source":"import java.net.URI;\nimport java.net.http.HttpClient;\nimport java.net.http.HttpRequest;\nimport java.net.http.HttpResponse;\n\nHttpClient client = HttpClient.newHttpClient();\n\nHttpRequest request = HttpRequest.newBuilder()\n    .uri(URI.create(\"https://api.revkeen.com/v2/events/11111111-1111-4111-8111-111111111111/resend\"))\n    .header(\"x-api-key\", System.getenv(\"REVKEEN_API_KEY\"))\n    .method(\"POST\", HttpRequest.BodyPublishers.noBody())\n    .build();\n\nHttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());\nSystem.out.println(response.body());"},{"lang":"ruby","label":"Ruby","source":"require \"net/http\"\nrequire \"json\"\nrequire \"uri\"\n\nuri = URI(\"https://api.revkeen.com/v2/events/11111111-1111-4111-8111-111111111111/resend\")\nhttp = Net::HTTP.new(uri.host, uri.port)\nhttp.use_ssl = true\n\nrequest = Net::HTTP::Post.new(uri)\nrequest[\"x-api-key\"] = ENV[\"REVKEEN_API_KEY\"]\n\nresponse = http.request(request)\nputs response.body"}],"security":[{"apiKey":["events:write"]},{"oauth2":["events:write"]}]}},"/usage-events":{"post":{"operationId":"usage_events_ingest","tags":["Usage Events"],"x-fern-sdk-group-name":["usage"],"x-fern-sdk-method-name":"ingest","summary":"Ingest usage events","description":"Send usage events for billing. Accepts a single event or a batch of up to 1000 events.\n\n**Single event:** Send the event object directly in the request body.\n**Batch:** Wrap events in an `events` array.\n\nEvents are matched to meters by `name` (event name) or `meter_id`. At least one of `customer_id`, `external_customer_id`, or `subscription_id` is required.\n\n**Idempotency:** provide `idempotency_key` per event. The key is unique per merchant forever; repeats return `duplicate` and do not create another billable event.\n\n**Attribution:** prefer `external_customer_id` when your system owns customer identifiers. Unknown external IDs fail fast instead of creating orphan usage.\n\n**Currency:** each subscription has one currency. Usage attributed to that subscription is interpreted in that currency; RevKeen does not perform FX conversion in the metering layer.\n\n---\n\n**Related endpoints**\n\n- `GET /usage-events` — Query usage events\n- `GET /usage-events/{id}` — Get a usage event\n\n**Common errors**\n\n- `400 invalid_request` — malformed payload or failed validation.\n- `401 authentication_error` — missing, invalid, expired, or revoked credential. Codes: `authentication_failed`, `invalid_api_key`, `expired_api_key`, `api_key_revoked`, `session_invalid`, `merchant_required`. Carries a `WWW-Authenticate: Bearer` challenge.\n- `413` — see the [errors reference](/docs/errors).\n\n**Idempotency**\n\nPass an `Idempotency-Key` header (UUID v4 recommended) to make retries safe. Keys are valid for 24 hours; see [the idempotency guide](/docs/fundamentals/idempotency).","security":[{"apiKey":["usage:write"]},{"apiKey":["prices:write"]}],"x-codeSamples":[{"lang":"curl","label":"cURL","source":"curl -X POST \"https://api.revkeen.com/v2/usage-events\" \\\n  -H \"x-api-key: $REVKEEN_API_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"name\": \"api_call\",\n    \"idempotency_key\": \"evt_unique_123\"\n  }'"},{"lang":"typescript","label":"TypeScript (SDK)","source":"// npm install @revkeen/sdk@1.20260822.1541\nimport { RevKeenClient } from \"@revkeen/sdk\";\nconst apiKey = process.env.REVKEEN_API_KEY;\nif (!apiKey) throw new Error(\"Set REVKEEN_API_KEY\");\nconst client = new RevKeenClient({ apiKey, baseUrl: \"https://api.revkeen.com/v2\" });\nconst result = await client.usage.ingest({\n  \"name\": \"api_call\",\n  \"idempotency_key\": \"evt_unique_123\"\n});\nconsole.log(result);"},{"lang":"go","label":"Go (HTTP)","source":"package main\nimport (\"fmt\"; \"io\"; \"net/http\"; \"os\"; \"strings\"; \"time\")\nfunc main() {\n  req, err := http.NewRequest(\"POST\", \"https://api.revkeen.com/v2/usage-events\", strings.NewReader(\"{\\\"name\\\":\\\"api_call\\\",\\\"idempotency_key\\\":\\\"evt_unique_123\\\"}\"))\n  if err != nil { panic(err) }\n  req.Header.Set(\"x-api-key\", os.Getenv(\"REVKEEN_API_KEY\"))\n  req.Header.Set(\"Content-Type\", \"application/json\")\n  client := &http.Client{Timeout: 10 * time.Second}\n  response, err := client.Do(req)\n  if err != nil { panic(err) }\n  defer response.Body.Close()\n  if response.StatusCode >= 400 { panic(fmt.Sprintf(\"RevKeen request failed (%d)\", response.StatusCode)) }\n  data, err := io.ReadAll(response.Body)\n  if err != nil { panic(err) }\n  fmt.Println(string(data))\n}"},{"lang":"php","label":"PHP (HTTP)","source":"<?php\n$request = curl_init(\"https://api.revkeen.com/v2/usage-events\");\ncurl_setopt_array($request, [\n  CURLOPT_CUSTOMREQUEST => \"POST\",\n  CURLOPT_RETURNTRANSFER => true,\n  CURLOPT_TIMEOUT => 10,\n  CURLOPT_HTTPHEADER => [\"x-api-key: \" . getenv(\"REVKEEN_API_KEY\"), \"Content-Type: application/json\"],\n  CURLOPT_POSTFIELDS => '{\\\"name\\\":\\\"api_call\\\",\\\"idempotency_key\\\":\\\"evt_unique_123\\\"}',\n]);\n$response = curl_exec($request);\nif ($response === false) throw new RuntimeException(curl_error($request));\n$status = curl_getinfo($request, CURLINFO_HTTP_CODE);\ncurl_close($request);\nif ($status >= 400) throw new RuntimeException(\"RevKeen request failed ($status)\");\necho $response;"},{"lang":"python","label":"Python","source":"import os\n\nimport requests\n\nresponse = requests.post(\n    \"https://api.revkeen.com/v2/usage-events\",\n    headers={\"x-api-key\": os.environ[\"REVKEEN_API_KEY\"]},\n    json={\n        \"name\": \"api_call\",\n        \"idempotency_key\": \"evt_unique_123\",\n    },\n)\n\nresponse.raise_for_status()\nprint(response.json())"},{"lang":"javascript","label":"JavaScript","source":"const credential = process.env.REVKEEN_API_KEY;\nif (!credential) throw new Error(\"Set REVKEEN_API_KEY\");\nconst response = await fetch(\"https://api.revkeen.com/v2/usage-events\", {\n  method: \"POST\",\n  headers: {\n    \"x-api-key\": credential,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n    \"name\": \"api_call\",\n    \"idempotency_key\": \"evt_unique_123\"\n  }),\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"java","label":"Java","source":"import java.net.URI;\nimport java.net.http.HttpClient;\nimport java.net.http.HttpRequest;\nimport java.net.http.HttpResponse;\n\nHttpClient client = HttpClient.newHttpClient();\n\nHttpRequest request = HttpRequest.newBuilder()\n    .uri(URI.create(\"https://api.revkeen.com/v2/usage-events\"))\n    .header(\"x-api-key\", System.getenv(\"REVKEEN_API_KEY\"))\n    .header(\"Content-Type\", \"application/json\")\n    .method(\"POST\", HttpRequest.BodyPublishers.ofString(\"\"\"\n        {\n          \"name\": \"api_call\",\n          \"idempotency_key\": \"evt_unique_123\"\n        }\n        \"\"\"))\n    .build();\n\nHttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());\nSystem.out.println(response.body());"},{"lang":"ruby","label":"Ruby","source":"require \"net/http\"\nrequire \"json\"\nrequire \"uri\"\n\nuri = URI(\"https://api.revkeen.com/v2/usage-events\")\nhttp = Net::HTTP.new(uri.host, uri.port)\nhttp.use_ssl = true\n\nrequest = Net::HTTP::Post.new(uri)\nrequest[\"x-api-key\"] = ENV[\"REVKEEN_API_KEY\"]\nrequest[\"Content-Type\"] = \"application/json\"\nrequest.body = {\n  \"name\" => \"api_call\",\n  \"idempotency_key\" => \"evt_unique_123\",\n}.to_json\n\nresponse = http.request(request)\nputs response.body"}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UsageIngestRequest"}}}},"responses":{"200":{"description":"Events deduplicated or skipped","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BatchIngestResult"}}}},"201":{"description":"All events ingested","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BatchIngestResult"}}}},"207":{"description":"Mixed results (partial success)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BatchIngestResult"}}}},"400":{"description":"Validation error or all events failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UsageEventError"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UsageEventError"}}},"headers":{"WWW-Authenticate":{"description":"RFC 6750 Bearer authentication challenge","schema":{"type":"string"},"example":"Bearer realm=\"revkeen-api\""}}},"413":{"description":"Request body exceeds 5 MiB","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UsageEventError"}}}},"429":{"description":"Too Many Requests - Rate limit exceeded","headers":{"Retry-After":{"description":"Whole seconds until the caller may retry","schema":{"type":"string"},"example":"60"},"X-RateLimit-Limit":{"description":"Request quota for the current window","schema":{"type":"string"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window","schema":{"type":"string"}},"X-RateLimit-Reset":{"description":"UNIX timestamp in seconds when the window resets","schema":{"type":"string"}}}}},"x-cli-group":"usage-events","x-cli-name":"ingest"},"get":{"operationId":"usage_events_list","tags":["Usage Events"],"x-fern-sdk-group-name":["usage"],"x-fern-sdk-method-name":"listEvents","summary":"Query usage events","description":"List usage events with optional filters. Returns up to 100 events per request.\n\n---\n\n**Related endpoints**\n\n- `POST /usage-events` — Ingest usage events\n- `GET /usage-events/{id}` — Get a usage event\n\n**Common errors**\n\n- `401 authentication_error` — missing, invalid, expired, or revoked credential. Codes: `authentication_failed`, `invalid_api_key`, `expired_api_key`, `api_key_revoked`, `session_invalid`, `merchant_required`. Carries a `WWW-Authenticate: Bearer` challenge.\n\n**Pagination**\n\nOffset-based with `limit` (default 25, max 100) and `offset`. The response `pagination` block includes `total` and `hasMore`. See [the pagination guide](/docs/fundamentals/pagination) for SDK auto-paging helpers.","security":[{"apiKey":["usage:read"]},{"apiKey":["prices:read"]}],"parameters":[{"schema":{"type":"string","description":"Filter by meter ID"},"required":false,"description":"Filter by meter ID","name":"meter_id","in":"query"},{"schema":{"type":"string","description":"Filter by customer ID"},"required":false,"description":"Filter by customer ID","name":"customer_id","in":"query"},{"schema":{"type":"string","description":"Filter by external customer ID"},"required":false,"description":"Filter by external customer ID","name":"external_customer_id","in":"query"},{"schema":{"type":"string","description":"Filter by subscription ID"},"required":false,"description":"Filter by subscription ID","name":"subscription_id","in":"query"},{"schema":{"type":"string","format":"date-time","description":"Start of time range (ISO 8601)","example":"2026-03-01T00:00:00Z"},"required":false,"description":"Start of time range (ISO 8601)","name":"start_time","in":"query"},{"schema":{"type":"string","format":"date-time","description":"End of time range (ISO 8601)","example":"2026-03-31T23:59:59Z"},"required":false,"description":"End of time range (ISO 8601)","name":"end_time","in":"query"},{"schema":{"type":"string","description":"Max results (default 100, max 100)","example":"100"},"required":false,"description":"Max results (default 100, max 100)","name":"limit","in":"query"}],"responses":{"200":{"description":"List of usage events","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UsageEventListResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UsageEventError"}}},"headers":{"WWW-Authenticate":{"description":"RFC 6750 Bearer authentication challenge","schema":{"type":"string"},"example":"Bearer realm=\"revkeen-api\""}}},"429":{"description":"Too Many Requests - Rate limit exceeded","headers":{"Retry-After":{"description":"Whole seconds until the caller may retry","schema":{"type":"string"},"example":"60"},"X-RateLimit-Limit":{"description":"Request quota for the current window","schema":{"type":"string"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window","schema":{"type":"string"}},"X-RateLimit-Reset":{"description":"UNIX timestamp in seconds when the window resets","schema":{"type":"string"}}}}},"x-cli-group":"usage-events","x-cli-name":"list","x-codeSamples":[{"lang":"curl","label":"cURL","source":"curl -X GET \"https://api.revkeen.com/v2/usage-events?meter_id=meter_xxx&customer_id=cus_1RxKm3ApBqCrDsEtFuGvHwJx\" \\\n  -H \"x-api-key: $REVKEEN_API_KEY\""},{"lang":"typescript","label":"TypeScript (HTTP)","source":"const credential = process.env.REVKEEN_API_KEY;\nif (!credential) throw new Error(\"Set REVKEEN_API_KEY\");\nconst response = await fetch(\"https://api.revkeen.com/v2/usage-events?meter_id=meter_xxx&customer_id=cus_1RxKm3ApBqCrDsEtFuGvHwJx\", {\n  method: \"GET\",\n  headers: {\n    \"x-api-key\": credential,\n  },\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"go","label":"Go (HTTP)","source":"package main\nimport (\"fmt\"; \"io\"; \"net/http\"; \"os\"; \"strings\"; \"time\")\nfunc main() {\n  req, err := http.NewRequest(\"GET\", \"https://api.revkeen.com/v2/usage-events?meter_id=meter_xxx&customer_id=cus_1RxKm3ApBqCrDsEtFuGvHwJx\", strings.NewReader(\"\"))\n  if err != nil { panic(err) }\n  req.Header.Set(\"x-api-key\", os.Getenv(\"REVKEEN_API_KEY\"))\n  req.Header.Set(\"Content-Type\", \"application/json\")\n  client := &http.Client{Timeout: 10 * time.Second}\n  response, err := client.Do(req)\n  if err != nil { panic(err) }\n  defer response.Body.Close()\n  if response.StatusCode >= 400 { panic(fmt.Sprintf(\"RevKeen request failed (%d)\", response.StatusCode)) }\n  data, err := io.ReadAll(response.Body)\n  if err != nil { panic(err) }\n  fmt.Println(string(data))\n}"},{"lang":"php","label":"PHP (HTTP)","source":"<?php\n$request = curl_init(\"https://api.revkeen.com/v2/usage-events?meter_id=meter_xxx&customer_id=cus_1RxKm3ApBqCrDsEtFuGvHwJx\");\ncurl_setopt_array($request, [\n  CURLOPT_CUSTOMREQUEST => \"GET\",\n  CURLOPT_RETURNTRANSFER => true,\n  CURLOPT_TIMEOUT => 10,\n  CURLOPT_HTTPHEADER => [\"x-api-key: \" . getenv(\"REVKEEN_API_KEY\"), \"Content-Type: application/json\"],\n]);\n$response = curl_exec($request);\nif ($response === false) throw new RuntimeException(curl_error($request));\n$status = curl_getinfo($request, CURLINFO_HTTP_CODE);\ncurl_close($request);\nif ($status >= 400) throw new RuntimeException(\"RevKeen request failed ($status)\");\necho $response;"},{"lang":"python","label":"Python","source":"import os\n\nimport requests\n\nresponse = requests.get(\n    \"https://api.revkeen.com/v2/usage-events\",\n    headers={\"x-api-key\": os.environ[\"REVKEEN_API_KEY\"]},\n    params={\n        \"meter_id\": \"meter_xxx\",\n        \"customer_id\": \"cus_1RxKm3ApBqCrDsEtFuGvHwJx\",\n    },\n)\n\nresponse.raise_for_status()\nprint(response.json())"},{"lang":"javascript","label":"JavaScript","source":"const credential = process.env.REVKEEN_API_KEY;\nif (!credential) throw new Error(\"Set REVKEEN_API_KEY\");\nconst response = await fetch(\"https://api.revkeen.com/v2/usage-events?meter_id=meter_xxx&customer_id=cus_1RxKm3ApBqCrDsEtFuGvHwJx\", {\n  method: \"GET\",\n  headers: {\n    \"x-api-key\": credential,\n  },\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"java","label":"Java","source":"import java.net.URI;\nimport java.net.http.HttpClient;\nimport java.net.http.HttpRequest;\nimport java.net.http.HttpResponse;\n\nHttpClient client = HttpClient.newHttpClient();\n\nHttpRequest request = HttpRequest.newBuilder()\n    .uri(URI.create(\"https://api.revkeen.com/v2/usage-events?meter_id=meter_xxx&customer_id=cus_1RxKm3ApBqCrDsEtFuGvHwJx\"))\n    .header(\"x-api-key\", System.getenv(\"REVKEEN_API_KEY\"))\n    .GET()\n    .build();\n\nHttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());\nSystem.out.println(response.body());"},{"lang":"ruby","label":"Ruby","source":"require \"net/http\"\nrequire \"json\"\nrequire \"uri\"\n\nuri = URI(\"https://api.revkeen.com/v2/usage-events?meter_id=meter_xxx&customer_id=cus_1RxKm3ApBqCrDsEtFuGvHwJx\")\nhttp = Net::HTTP.new(uri.host, uri.port)\nhttp.use_ssl = true\n\nrequest = Net::HTTP::Get.new(uri)\nrequest[\"x-api-key\"] = ENV[\"REVKEEN_API_KEY\"]\n\nresponse = http.request(request)\nputs response.body"}]}},"/usage-events/{id}":{"get":{"operationId":"usage_events_get","tags":["Usage Events"],"x-fern-sdk-group-name":["usage"],"x-fern-sdk-method-name":"getEvent","summary":"Get a usage event","description":"Retrieve a single usage event by ID. Unknown ids and ids that belong to another merchant both return 404 — the lookup is merchant-scoped under RLS.\n\n---\n\n**Related endpoints**\n\n- `POST /usage-events` — Ingest usage events\n- `GET /usage-events` — Query usage events\n\n**Common errors**\n\n- `401 authentication_error` — missing, invalid, expired, or revoked credential. Codes: `authentication_failed`, `invalid_api_key`, `expired_api_key`, `api_key_revoked`, `session_invalid`, `merchant_required`. Carries a `WWW-Authenticate: Bearer` challenge.\n- `404 resource_missing` — the referenced resource does not exist or is not visible to your key.","security":[{"apiKey":["usage:read"]},{"apiKey":["prices:read"]}],"parameters":[{"schema":{"type":"string","format":"uuid","description":"Usage event ID"},"required":true,"description":"Usage event ID","name":"id","in":"path"}],"responses":{"200":{"description":"Usage event","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UsageEventGetResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UsageEventError"}}},"headers":{"WWW-Authenticate":{"description":"RFC 6750 Bearer authentication challenge","schema":{"type":"string"},"example":"Bearer realm=\"revkeen-api\""}}},"404":{"description":"Event not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UsageEventError"}}}},"429":{"description":"Too Many Requests - Rate limit exceeded","headers":{"Retry-After":{"description":"Whole seconds until the caller may retry","schema":{"type":"string"},"example":"60"},"X-RateLimit-Limit":{"description":"Request quota for the current window","schema":{"type":"string"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window","schema":{"type":"string"}},"X-RateLimit-Reset":{"description":"UNIX timestamp in seconds when the window resets","schema":{"type":"string"}}}}},"x-cli-group":"usage-events","x-cli-name":"get","x-codeSamples":[{"lang":"curl","label":"cURL","source":"curl -X GET \"https://api.revkeen.com/v2/usage-events/11111111-1111-4111-8111-111111111111\" \\\n  -H \"x-api-key: $REVKEEN_API_KEY\""},{"lang":"typescript","label":"TypeScript (HTTP)","source":"const credential = process.env.REVKEEN_API_KEY;\nif (!credential) throw new Error(\"Set REVKEEN_API_KEY\");\nconst response = await fetch(\"https://api.revkeen.com/v2/usage-events/11111111-1111-4111-8111-111111111111\", {\n  method: \"GET\",\n  headers: {\n    \"x-api-key\": credential,\n  },\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"go","label":"Go (HTTP)","source":"package main\nimport (\"fmt\"; \"io\"; \"net/http\"; \"os\"; \"strings\"; \"time\")\nfunc main() {\n  req, err := http.NewRequest(\"GET\", \"https://api.revkeen.com/v2/usage-events/11111111-1111-4111-8111-111111111111\", strings.NewReader(\"\"))\n  if err != nil { panic(err) }\n  req.Header.Set(\"x-api-key\", os.Getenv(\"REVKEEN_API_KEY\"))\n  req.Header.Set(\"Content-Type\", \"application/json\")\n  client := &http.Client{Timeout: 10 * time.Second}\n  response, err := client.Do(req)\n  if err != nil { panic(err) }\n  defer response.Body.Close()\n  if response.StatusCode >= 400 { panic(fmt.Sprintf(\"RevKeen request failed (%d)\", response.StatusCode)) }\n  data, err := io.ReadAll(response.Body)\n  if err != nil { panic(err) }\n  fmt.Println(string(data))\n}"},{"lang":"php","label":"PHP (HTTP)","source":"<?php\n$request = curl_init(\"https://api.revkeen.com/v2/usage-events/11111111-1111-4111-8111-111111111111\");\ncurl_setopt_array($request, [\n  CURLOPT_CUSTOMREQUEST => \"GET\",\n  CURLOPT_RETURNTRANSFER => true,\n  CURLOPT_TIMEOUT => 10,\n  CURLOPT_HTTPHEADER => [\"x-api-key: \" . getenv(\"REVKEEN_API_KEY\"), \"Content-Type: application/json\"],\n]);\n$response = curl_exec($request);\nif ($response === false) throw new RuntimeException(curl_error($request));\n$status = curl_getinfo($request, CURLINFO_HTTP_CODE);\ncurl_close($request);\nif ($status >= 400) throw new RuntimeException(\"RevKeen request failed ($status)\");\necho $response;"},{"lang":"python","label":"Python","source":"import os\n\nimport requests\n\nresponse = requests.get(\n    \"https://api.revkeen.com/v2/usage-events/11111111-1111-4111-8111-111111111111\",\n    headers={\"x-api-key\": os.environ[\"REVKEEN_API_KEY\"]},\n)\n\nresponse.raise_for_status()\nprint(response.json())"},{"lang":"javascript","label":"JavaScript","source":"const credential = process.env.REVKEEN_API_KEY;\nif (!credential) throw new Error(\"Set REVKEEN_API_KEY\");\nconst response = await fetch(\"https://api.revkeen.com/v2/usage-events/11111111-1111-4111-8111-111111111111\", {\n  method: \"GET\",\n  headers: {\n    \"x-api-key\": credential,\n  },\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"java","label":"Java","source":"import java.net.URI;\nimport java.net.http.HttpClient;\nimport java.net.http.HttpRequest;\nimport java.net.http.HttpResponse;\n\nHttpClient client = HttpClient.newHttpClient();\n\nHttpRequest request = HttpRequest.newBuilder()\n    .uri(URI.create(\"https://api.revkeen.com/v2/usage-events/11111111-1111-4111-8111-111111111111\"))\n    .header(\"x-api-key\", System.getenv(\"REVKEEN_API_KEY\"))\n    .GET()\n    .build();\n\nHttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());\nSystem.out.println(response.body());"},{"lang":"ruby","label":"Ruby","source":"require \"net/http\"\nrequire \"json\"\nrequire \"uri\"\n\nuri = URI(\"https://api.revkeen.com/v2/usage-events/11111111-1111-4111-8111-111111111111\")\nhttp = Net::HTTP.new(uri.host, uri.port)\nhttp.use_ssl = true\n\nrequest = Net::HTTP::Get.new(uri)\nrequest[\"x-api-key\"] = ENV[\"REVKEEN_API_KEY\"]\n\nresponse = http.request(request)\nputs response.body"}]}},"/usage/balance":{"get":{"operationId":"usage_balance_get","tags":["Usage"],"summary":"Get current usage balance","description":"Returns the current usage balance for a customer or subscription in the active billing period. Shows per-meter breakdown with included allowance, overage, and estimated charges. A subscription has one currency; metered usage attributed to the subscription is assumed to be in that same currency. RevKeen does not perform FX conversion in the metering layer.\n\n**Required:** at least one of `subscription_id`, `customer_id` or `external_customer_id`. Each is optional on its own, but a request with none of them is rejected with `400` and `error.type` `invalid_request_error`. `meter_id` narrows the result and does not identify a customer by itself.\n\n---\n\n**Common errors**\n\n- `400 invalid_request` — malformed payload or failed validation.\n- `401 authentication_error` — missing, invalid, expired, or revoked credential. Codes: `authentication_failed`, `invalid_api_key`, `expired_api_key`, `api_key_revoked`, `session_invalid`, `merchant_required`. Carries a `WWW-Authenticate: Bearer` challenge.","x-fern-sdk-group-name":["usage"],"x-fern-sdk-method-name":"getBalance","security":[{"apiKey":["usage:read"]},{"apiKey":["prices:read"]}],"parameters":[{"schema":{"type":"string","format":"uuid","description":"Filter to a specific subscription","example":"11111111-1111-4111-8111-111111111111"},"required":false,"description":"Filter to a specific subscription","name":"subscription_id","in":"query"},{"schema":{"type":"string","format":"uuid","description":"Filter to a specific customer by RevKeen ID"},"required":false,"description":"Filter to a specific customer by RevKeen ID","name":"customer_id","in":"query"},{"schema":{"type":"string","description":"Filter to a customer by your external identifier","example":"usr_123"},"required":false,"description":"Filter to a customer by your external identifier","name":"external_customer_id","in":"query"},{"schema":{"type":"string","format":"uuid","description":"Filter to a single meter"},"required":false,"description":"Filter to a single meter","name":"meter_id","in":"query"}],"responses":{"200":{"description":"Usage balance retrieved successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UsageBalanceResponse"}}}},"400":{"description":"Invalid request — at least one identifier is required","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"$ref":"#/components/schemas/UsageBalanceGet400ResponseError"}},"required":["error"]}}}},"401":{"description":"Authentication required","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"$ref":"#/components/schemas/UsageBalanceGet400ResponseError"}},"required":["error"]}}},"headers":{"WWW-Authenticate":{"description":"RFC 6750 Bearer authentication challenge","schema":{"type":"string"},"example":"Bearer realm=\"revkeen-api\""}}},"429":{"description":"Too Many Requests - Rate limit exceeded","headers":{"Retry-After":{"description":"Whole seconds until the caller may retry","schema":{"type":"string"},"example":"60"},"X-RateLimit-Limit":{"description":"Request quota for the current window","schema":{"type":"string"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window","schema":{"type":"string"}},"X-RateLimit-Reset":{"description":"UNIX timestamp in seconds when the window resets","schema":{"type":"string"}}}}},"x-cli-group":"usage","x-cli-name":"balance-get","x-codeSamples":[{"lang":"curl","label":"cURL","source":"curl -X GET \"https://api.revkeen.com/v2/usage/balance?subscription_id=11111111-1111-4111-8111-111111111111&customer_id=11111111-1111-4111-8111-111111111111\" \\\n  -H \"x-api-key: $REVKEEN_API_KEY\""},{"lang":"typescript","label":"TypeScript (HTTP)","source":"const credential = process.env.REVKEEN_API_KEY;\nif (!credential) throw new Error(\"Set REVKEEN_API_KEY\");\nconst response = await fetch(\"https://api.revkeen.com/v2/usage/balance?subscription_id=11111111-1111-4111-8111-111111111111&customer_id=11111111-1111-4111-8111-111111111111\", {\n  method: \"GET\",\n  headers: {\n    \"x-api-key\": credential,\n  },\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"go","label":"Go (HTTP)","source":"package main\nimport (\"fmt\"; \"io\"; \"net/http\"; \"os\"; \"strings\"; \"time\")\nfunc main() {\n  req, err := http.NewRequest(\"GET\", \"https://api.revkeen.com/v2/usage/balance?subscription_id=11111111-1111-4111-8111-111111111111&customer_id=11111111-1111-4111-8111-111111111111\", strings.NewReader(\"\"))\n  if err != nil { panic(err) }\n  req.Header.Set(\"x-api-key\", os.Getenv(\"REVKEEN_API_KEY\"))\n  req.Header.Set(\"Content-Type\", \"application/json\")\n  client := &http.Client{Timeout: 10 * time.Second}\n  response, err := client.Do(req)\n  if err != nil { panic(err) }\n  defer response.Body.Close()\n  if response.StatusCode >= 400 { panic(fmt.Sprintf(\"RevKeen request failed (%d)\", response.StatusCode)) }\n  data, err := io.ReadAll(response.Body)\n  if err != nil { panic(err) }\n  fmt.Println(string(data))\n}"},{"lang":"php","label":"PHP (HTTP)","source":"<?php\n$request = curl_init(\"https://api.revkeen.com/v2/usage/balance?subscription_id=11111111-1111-4111-8111-111111111111&customer_id=11111111-1111-4111-8111-111111111111\");\ncurl_setopt_array($request, [\n  CURLOPT_CUSTOMREQUEST => \"GET\",\n  CURLOPT_RETURNTRANSFER => true,\n  CURLOPT_TIMEOUT => 10,\n  CURLOPT_HTTPHEADER => [\"x-api-key: \" . getenv(\"REVKEEN_API_KEY\"), \"Content-Type: application/json\"],\n]);\n$response = curl_exec($request);\nif ($response === false) throw new RuntimeException(curl_error($request));\n$status = curl_getinfo($request, CURLINFO_HTTP_CODE);\ncurl_close($request);\nif ($status >= 400) throw new RuntimeException(\"RevKeen request failed ($status)\");\necho $response;"},{"lang":"python","label":"Python","source":"import os\n\nimport requests\n\nresponse = requests.get(\n    \"https://api.revkeen.com/v2/usage/balance\",\n    headers={\"x-api-key\": os.environ[\"REVKEEN_API_KEY\"]},\n    params={\n        \"subscription_id\": \"11111111-1111-4111-8111-111111111111\",\n        \"customer_id\": \"11111111-1111-4111-8111-111111111111\",\n    },\n)\n\nresponse.raise_for_status()\nprint(response.json())"},{"lang":"javascript","label":"JavaScript","source":"const credential = process.env.REVKEEN_API_KEY;\nif (!credential) throw new Error(\"Set REVKEEN_API_KEY\");\nconst response = await fetch(\"https://api.revkeen.com/v2/usage/balance?subscription_id=11111111-1111-4111-8111-111111111111&customer_id=11111111-1111-4111-8111-111111111111\", {\n  method: \"GET\",\n  headers: {\n    \"x-api-key\": credential,\n  },\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"java","label":"Java","source":"import java.net.URI;\nimport java.net.http.HttpClient;\nimport java.net.http.HttpRequest;\nimport java.net.http.HttpResponse;\n\nHttpClient client = HttpClient.newHttpClient();\n\nHttpRequest request = HttpRequest.newBuilder()\n    .uri(URI.create(\"https://api.revkeen.com/v2/usage/balance?subscription_id=11111111-1111-4111-8111-111111111111&customer_id=11111111-1111-4111-8111-111111111111\"))\n    .header(\"x-api-key\", System.getenv(\"REVKEEN_API_KEY\"))\n    .GET()\n    .build();\n\nHttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());\nSystem.out.println(response.body());"},{"lang":"ruby","label":"Ruby","source":"require \"net/http\"\nrequire \"json\"\nrequire \"uri\"\n\nuri = URI(\"https://api.revkeen.com/v2/usage/balance?subscription_id=11111111-1111-4111-8111-111111111111&customer_id=11111111-1111-4111-8111-111111111111\")\nhttp = Net::HTTP.new(uri.host, uri.port)\nhttp.use_ssl = true\n\nrequest = Net::HTTP::Get.new(uri)\nrequest[\"x-api-key\"] = ENV[\"REVKEEN_API_KEY\"]\n\nresponse = http.request(request)\nputs response.body"}]}},"/entitlements":{"get":{"operationId":"entitlements_list","tags":["Entitlements"],"x-fern-sdk-group-name":["entitlements"],"summary":"List entitlements","description":"Retrieve all entitlements for a customer. Pass `customer_id` as a query parameter. Includes computed access status based on subscription state.\n\n---\n\n**Related endpoints**\n\n- `GET /entitlements/check` — Check entitlement access\n\n**Common errors**\n\n- `400 invalid_request` — malformed payload or failed validation.\n- `401 authentication_error` — missing, invalid, expired, or revoked credential. Codes: `authentication_failed`, `invalid_api_key`, `expired_api_key`, `api_key_revoked`, `session_invalid`, `merchant_required`. Carries a `WWW-Authenticate: Bearer` challenge.\n- `404 resource_missing` — the referenced resource does not exist or is not visible to your key.\n\n**Pagination**\n\nOffset-based with `limit` (default 25, max 100) and `offset`. The response `pagination` block includes `total` and `hasMore`. See [the pagination guide](/docs/fundamentals/pagination) for SDK auto-paging helpers.","parameters":[{"schema":{"type":"string","format":"uuid","description":"Customer UUID (required)"},"required":true,"description":"Customer UUID (required)","name":"customer_id","in":"query"},{"schema":{"type":["boolean","null"],"default":false,"description":"Include expired entitlements"},"required":false,"description":"Include expired entitlements","name":"include_expired","in":"query"},{"schema":{"type":"string","description":"Filter by benefit type"},"required":false,"description":"Filter by benefit type","name":"benefit_type","in":"query"},{"schema":{"type":"string","description":"Filter by category"},"required":false,"description":"Filter by category","name":"category","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":100,"default":50,"description":"Maximum results (1-100)"},"required":false,"description":"Maximum results (1-100)","name":"limit","in":"query"},{"schema":{"type":["integer","null"],"minimum":0,"default":0,"description":"Results to skip"},"required":false,"description":"Results to skip","name":"offset","in":"query"}],"responses":{"200":{"description":"List of customer entitlements","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EntitlementListResponse"}}}},"400":{"description":"Missing customer_id"},"401":{"description":"Unauthorized","headers":{"WWW-Authenticate":{"description":"RFC 6750 Bearer authentication challenge","schema":{"type":"string"},"example":"Bearer realm=\"revkeen-api\""}}},"404":{"description":"Customer not found"},"429":{"description":"Too Many Requests - Rate limit exceeded","headers":{"Retry-After":{"description":"Whole seconds until the caller may retry","schema":{"type":"string"},"example":"60"},"X-RateLimit-Limit":{"description":"Request quota for the current window","schema":{"type":"string"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window","schema":{"type":"string"}},"X-RateLimit-Reset":{"description":"UNIX timestamp in seconds when the window resets","schema":{"type":"string"}}}}},"x-cli-group":"entitlements","x-cli-name":"list","x-codeSamples":[{"lang":"curl","label":"cURL","source":"curl -X GET \"https://api.revkeen.com/v2/entitlements?customer_id=11111111-1111-4111-8111-111111111111&include_expired=false&benefit_type=example&category=example\" \\\n  -H \"x-api-key: $REVKEEN_API_KEY\""},{"lang":"typescript","label":"TypeScript (SDK)","source":"// npm install @revkeen/sdk@1.20260822.1541\nimport { RevKeenClient } from \"@revkeen/sdk\";\nconst apiKey = process.env.REVKEEN_API_KEY;\nif (!apiKey) throw new Error(\"Set REVKEEN_API_KEY\");\nconst client = new RevKeenClient({ apiKey, baseUrl: \"https://api.revkeen.com/v2\" });\nconst result = await client.entitlements.list({\n  \"customer_id\": \"11111111-1111-4111-8111-111111111111\",\n  \"include_expired\": false,\n  \"benefit_type\": \"example\",\n  \"category\": \"example\"\n});\nconsole.log(result);"},{"lang":"go","label":"Go (HTTP)","source":"package main\nimport (\"fmt\"; \"io\"; \"net/http\"; \"os\"; \"strings\"; \"time\")\nfunc main() {\n  req, err := http.NewRequest(\"GET\", \"https://api.revkeen.com/v2/entitlements?customer_id=11111111-1111-4111-8111-111111111111&include_expired=false&benefit_type=example&category=example\", strings.NewReader(\"\"))\n  if err != nil { panic(err) }\n  req.Header.Set(\"x-api-key\", os.Getenv(\"REVKEEN_API_KEY\"))\n  req.Header.Set(\"Content-Type\", \"application/json\")\n  client := &http.Client{Timeout: 10 * time.Second}\n  response, err := client.Do(req)\n  if err != nil { panic(err) }\n  defer response.Body.Close()\n  if response.StatusCode >= 400 { panic(fmt.Sprintf(\"RevKeen request failed (%d)\", response.StatusCode)) }\n  data, err := io.ReadAll(response.Body)\n  if err != nil { panic(err) }\n  fmt.Println(string(data))\n}"},{"lang":"php","label":"PHP (HTTP)","source":"<?php\n$request = curl_init(\"https://api.revkeen.com/v2/entitlements?customer_id=11111111-1111-4111-8111-111111111111&include_expired=false&benefit_type=example&category=example\");\ncurl_setopt_array($request, [\n  CURLOPT_CUSTOMREQUEST => \"GET\",\n  CURLOPT_RETURNTRANSFER => true,\n  CURLOPT_TIMEOUT => 10,\n  CURLOPT_HTTPHEADER => [\"x-api-key: \" . getenv(\"REVKEEN_API_KEY\"), \"Content-Type: application/json\"],\n]);\n$response = curl_exec($request);\nif ($response === false) throw new RuntimeException(curl_error($request));\n$status = curl_getinfo($request, CURLINFO_HTTP_CODE);\ncurl_close($request);\nif ($status >= 400) throw new RuntimeException(\"RevKeen request failed ($status)\");\necho $response;"},{"lang":"python","label":"Python","source":"import os\n\nimport requests\n\nresponse = requests.get(\n    \"https://api.revkeen.com/v2/entitlements\",\n    headers={\"x-api-key\": os.environ[\"REVKEEN_API_KEY\"]},\n    params={\n        \"customer_id\": \"11111111-1111-4111-8111-111111111111\",\n        \"include_expired\": False,\n        \"benefit_type\": \"example\",\n        \"category\": \"example\",\n    },\n)\n\nresponse.raise_for_status()\nprint(response.json())"},{"lang":"javascript","label":"JavaScript","source":"const credential = process.env.REVKEEN_API_KEY;\nif (!credential) throw new Error(\"Set REVKEEN_API_KEY\");\nconst response = await fetch(\"https://api.revkeen.com/v2/entitlements?customer_id=11111111-1111-4111-8111-111111111111&include_expired=false&benefit_type=example&category=example\", {\n  method: \"GET\",\n  headers: {\n    \"x-api-key\": credential,\n  },\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"java","label":"Java","source":"import java.net.URI;\nimport java.net.http.HttpClient;\nimport java.net.http.HttpRequest;\nimport java.net.http.HttpResponse;\n\nHttpClient client = HttpClient.newHttpClient();\n\nHttpRequest request = HttpRequest.newBuilder()\n    .uri(URI.create(\"https://api.revkeen.com/v2/entitlements?customer_id=11111111-1111-4111-8111-111111111111&include_expired=false&benefit_type=example&category=example\"))\n    .header(\"x-api-key\", System.getenv(\"REVKEEN_API_KEY\"))\n    .GET()\n    .build();\n\nHttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());\nSystem.out.println(response.body());"},{"lang":"ruby","label":"Ruby","source":"require \"net/http\"\nrequire \"json\"\nrequire \"uri\"\n\nuri = URI(\"https://api.revkeen.com/v2/entitlements?customer_id=11111111-1111-4111-8111-111111111111&include_expired=false&benefit_type=example&category=example\")\nhttp = Net::HTTP.new(uri.host, uri.port)\nhttp.use_ssl = true\n\nrequest = Net::HTTP::Get.new(uri)\nrequest[\"x-api-key\"] = ENV[\"REVKEEN_API_KEY\"]\n\nresponse = http.request(request)\nputs response.body"}],"security":[{"apiKey":["customers:read"]},{"oauth2":["customers:read"]}]}},"/entitlements/check":{"get":{"operationId":"entitlements_check","tags":["Entitlements"],"x-fern-sdk-group-name":["entitlements"],"summary":"Check entitlement access","description":"Check if a customer has access to a specific benefit by key. This is the primary endpoint for feature gating and licensing checks.\n\n---\n\n**Related endpoints**\n\n- `GET /entitlements` — List entitlements\n\n**Common errors**\n\n- `401 authentication_error` — missing, invalid, expired, or revoked credential. Codes: `authentication_failed`, `invalid_api_key`, `expired_api_key`, `api_key_revoked`, `session_invalid`, `merchant_required`. Carries a `WWW-Authenticate: Bearer` challenge.\n- `404 resource_missing` — the referenced resource does not exist or is not visible to your key.","parameters":[{"schema":{"type":"string","format":"uuid","description":"Customer UUID (required)"},"required":true,"description":"Customer UUID (required)","name":"customer_id","in":"query"},{"schema":{"type":"string","minLength":1,"description":"Benefit key to check"},"required":true,"description":"Benefit key to check","name":"benefit_key","in":"query"}],"responses":{"200":{"description":"Entitlement check result","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EntitlementCheckResponse"}}}},"401":{"description":"Unauthorized","headers":{"WWW-Authenticate":{"description":"RFC 6750 Bearer authentication challenge","schema":{"type":"string"},"example":"Bearer realm=\"revkeen-api\""}}},"404":{"description":"Customer not found"},"429":{"description":"Too Many Requests - Rate limit exceeded","headers":{"Retry-After":{"description":"Whole seconds until the caller may retry","schema":{"type":"string"},"example":"60"},"X-RateLimit-Limit":{"description":"Request quota for the current window","schema":{"type":"string"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window","schema":{"type":"string"}},"X-RateLimit-Reset":{"description":"UNIX timestamp in seconds when the window resets","schema":{"type":"string"}}}}},"x-cli-group":"entitlements","x-cli-name":"check","x-codeSamples":[{"lang":"curl","label":"cURL","source":"curl -X GET \"https://api.revkeen.com/v2/entitlements/check?customer_id=11111111-1111-4111-8111-111111111111&benefit_key=example\" \\\n  -H \"x-api-key: $REVKEEN_API_KEY\""},{"lang":"typescript","label":"TypeScript (SDK)","source":"// npm install @revkeen/sdk@1.20260822.1541\nimport { RevKeenClient } from \"@revkeen/sdk\";\nconst apiKey = process.env.REVKEEN_API_KEY;\nif (!apiKey) throw new Error(\"Set REVKEEN_API_KEY\");\nconst client = new RevKeenClient({ apiKey, baseUrl: \"https://api.revkeen.com/v2\" });\nconst result = await client.entitlements.check({\n  \"customer_id\": \"11111111-1111-4111-8111-111111111111\",\n  \"benefit_key\": \"example\"\n});\nconsole.log(result);"},{"lang":"go","label":"Go (HTTP)","source":"package main\nimport (\"fmt\"; \"io\"; \"net/http\"; \"os\"; \"strings\"; \"time\")\nfunc main() {\n  req, err := http.NewRequest(\"GET\", \"https://api.revkeen.com/v2/entitlements/check?customer_id=11111111-1111-4111-8111-111111111111&benefit_key=example\", strings.NewReader(\"\"))\n  if err != nil { panic(err) }\n  req.Header.Set(\"x-api-key\", os.Getenv(\"REVKEEN_API_KEY\"))\n  req.Header.Set(\"Content-Type\", \"application/json\")\n  client := &http.Client{Timeout: 10 * time.Second}\n  response, err := client.Do(req)\n  if err != nil { panic(err) }\n  defer response.Body.Close()\n  if response.StatusCode >= 400 { panic(fmt.Sprintf(\"RevKeen request failed (%d)\", response.StatusCode)) }\n  data, err := io.ReadAll(response.Body)\n  if err != nil { panic(err) }\n  fmt.Println(string(data))\n}"},{"lang":"php","label":"PHP (HTTP)","source":"<?php\n$request = curl_init(\"https://api.revkeen.com/v2/entitlements/check?customer_id=11111111-1111-4111-8111-111111111111&benefit_key=example\");\ncurl_setopt_array($request, [\n  CURLOPT_CUSTOMREQUEST => \"GET\",\n  CURLOPT_RETURNTRANSFER => true,\n  CURLOPT_TIMEOUT => 10,\n  CURLOPT_HTTPHEADER => [\"x-api-key: \" . getenv(\"REVKEEN_API_KEY\"), \"Content-Type: application/json\"],\n]);\n$response = curl_exec($request);\nif ($response === false) throw new RuntimeException(curl_error($request));\n$status = curl_getinfo($request, CURLINFO_HTTP_CODE);\ncurl_close($request);\nif ($status >= 400) throw new RuntimeException(\"RevKeen request failed ($status)\");\necho $response;"},{"lang":"python","label":"Python","source":"import os\n\nimport requests\n\nresponse = requests.get(\n    \"https://api.revkeen.com/v2/entitlements/check\",\n    headers={\"x-api-key\": os.environ[\"REVKEEN_API_KEY\"]},\n    params={\n        \"customer_id\": \"11111111-1111-4111-8111-111111111111\",\n        \"benefit_key\": \"example\",\n    },\n)\n\nresponse.raise_for_status()\nprint(response.json())"},{"lang":"javascript","label":"JavaScript","source":"const credential = process.env.REVKEEN_API_KEY;\nif (!credential) throw new Error(\"Set REVKEEN_API_KEY\");\nconst response = await fetch(\"https://api.revkeen.com/v2/entitlements/check?customer_id=11111111-1111-4111-8111-111111111111&benefit_key=example\", {\n  method: \"GET\",\n  headers: {\n    \"x-api-key\": credential,\n  },\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"java","label":"Java","source":"import java.net.URI;\nimport java.net.http.HttpClient;\nimport java.net.http.HttpRequest;\nimport java.net.http.HttpResponse;\n\nHttpClient client = HttpClient.newHttpClient();\n\nHttpRequest request = HttpRequest.newBuilder()\n    .uri(URI.create(\"https://api.revkeen.com/v2/entitlements/check?customer_id=11111111-1111-4111-8111-111111111111&benefit_key=example\"))\n    .header(\"x-api-key\", System.getenv(\"REVKEEN_API_KEY\"))\n    .GET()\n    .build();\n\nHttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());\nSystem.out.println(response.body());"},{"lang":"ruby","label":"Ruby","source":"require \"net/http\"\nrequire \"json\"\nrequire \"uri\"\n\nuri = URI(\"https://api.revkeen.com/v2/entitlements/check?customer_id=11111111-1111-4111-8111-111111111111&benefit_key=example\")\nhttp = Net::HTTP.new(uri.host, uri.port)\nhttp.use_ssl = true\n\nrequest = Net::HTTP::Get.new(uri)\nrequest[\"x-api-key\"] = ENV[\"REVKEEN_API_KEY\"]\n\nresponse = http.request(request)\nputs response.body"}],"security":[{"apiKey":["customers:read"]},{"oauth2":["customers:read"]}]}},"/transactions":{"get":{"operationId":"transactions_list","tags":["Transactions"],"x-fern-sdk-group-name":["transactions"],"summary":"List transactions","description":"List all transactions for the authenticated merchant. Transactions represent the unified financial truth — every sale, refund, void, capture, dispute, and adjustment.\n\n`starting_after` and `ending_before` are mutually exclusive: supply at most one. Sending both is rejected with 400 `parameter_invalid`. A cursor must also name a transaction belonging to the authenticated merchant; an unknown id is rejected with 400 `resource_missing`.\n\n---\n\n**Related endpoints**\n\n- `GET /transactions/{id}` — Get transaction by ID\n\n**Common errors**\n\n- `400 invalid_request` — malformed payload or failed validation.\n- `401 authentication_error` — missing, invalid, expired, or revoked credential. Codes: `authentication_failed`, `invalid_api_key`, `expired_api_key`, `api_key_revoked`, `session_invalid`, `merchant_required`. Carries a `WWW-Authenticate: Bearer` challenge.\n\n**Pagination**\n\nOffset-based with `limit` (default 25, max 100) and `offset`. The response `pagination` block includes `total` and `hasMore`. See [the pagination guide](/docs/fundamentals/pagination) for SDK auto-paging helpers.","parameters":[{"schema":{"type":"string","enum":["sale","refund","void","capture","dispute","adjustment"],"description":"Filter by transaction type"},"required":false,"description":"Filter by transaction type","name":"type","in":"query"},{"schema":{"type":"string","enum":["pending","succeeded","failed","voided"],"description":"Filter by status"},"required":false,"description":"Filter by status","name":"status","in":"query"},{"schema":{"type":"string","format":"uuid","description":"Filter by customer ID"},"required":false,"description":"Filter by customer ID","name":"customer_id","in":"query"},{"schema":{"type":"string","format":"uuid","description":"Filter by invoice ID"},"required":false,"description":"Filter by invoice ID","name":"invoice_id","in":"query"},{"schema":{"type":["number","null"],"minimum":0,"maximum":253402300799,"description":"Filter by created_at >= (Unix timestamp)"},"required":false,"description":"Filter by created_at >= (Unix timestamp)","name":"created_gte","in":"query"},{"schema":{"type":["number","null"],"minimum":0,"maximum":253402300799,"description":"Filter by created_at <= (Unix timestamp)"},"required":false,"description":"Filter by created_at <= (Unix timestamp)","name":"created_lte","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":100,"default":20,"description":"Maximum number of results (1-100)"},"required":false,"description":"Maximum number of results (1-100)","name":"limit","in":"query"},{"schema":{"type":"string","format":"uuid","description":"Cursor for pagination: return transactions after this transaction ID. Mutually exclusive with `ending_before` — supplying both is rejected with 400 `parameter_invalid`. The ID must name a transaction of the authenticated merchant, or the request is rejected with 400 `resource_missing`."},"required":false,"description":"Cursor for pagination: return transactions after this transaction ID. Mutually exclusive with `ending_before` — supplying both is rejected with 400 `parameter_invalid`. The ID must name a transaction of the authenticated merchant, or the request is rejected with 400 `resource_missing`.","name":"starting_after","in":"query"},{"schema":{"type":"string","format":"uuid","description":"Cursor for pagination: return transactions before this transaction ID. Mutually exclusive with `starting_after` — supplying both is rejected with 400 `parameter_invalid`. The ID must name a transaction of the authenticated merchant, or the request is rejected with 400 `resource_missing`."},"required":false,"description":"Cursor for pagination: return transactions before this transaction ID. Mutually exclusive with `starting_after` — supplying both is rejected with 400 `parameter_invalid`. The ID must name a transaction of the authenticated merchant, or the request is rejected with 400 `resource_missing`.","name":"ending_before","in":"query"}],"responses":{"200":{"description":"List of transactions","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TransactionListResponse"}}}},"400":{"description":"`starting_after` and `ending_before` were combined, or a cursor named no transaction of this merchant","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListCursorError"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TransactionListResponse"}}},"headers":{"WWW-Authenticate":{"description":"RFC 6750 Bearer authentication challenge","schema":{"type":"string"},"example":"Bearer realm=\"revkeen-api\""}}},"429":{"description":"Too Many Requests - Rate limit exceeded","headers":{"Retry-After":{"description":"Whole seconds until the caller may retry","schema":{"type":"string"},"example":"60"},"X-RateLimit-Limit":{"description":"Request quota for the current window","schema":{"type":"string"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window","schema":{"type":"string"}},"X-RateLimit-Reset":{"description":"UNIX timestamp in seconds when the window resets","schema":{"type":"string"}}}}},"x-cli-group":"transactions","x-cli-name":"list","x-codeSamples":[{"lang":"curl","label":"cURL","source":"curl -X GET \"https://api.revkeen.com/v2/transactions?type=sale&status=pending\" \\\n  -H \"x-api-key: $REVKEEN_API_KEY\""},{"lang":"typescript","label":"TypeScript (SDK)","source":"// npm install @revkeen/sdk@1.20260822.1541\nimport { RevKeenClient } from \"@revkeen/sdk\";\nconst apiKey = process.env.REVKEEN_API_KEY;\nif (!apiKey) throw new Error(\"Set REVKEEN_API_KEY\");\nconst client = new RevKeenClient({ apiKey, baseUrl: \"https://api.revkeen.com/v2\" });\nconst result = await client.transactions.list({\n  \"type\": \"sale\",\n  \"status\": \"pending\"\n});\nconsole.log(result);"},{"lang":"go","label":"Go (HTTP)","source":"package main\nimport (\"fmt\"; \"io\"; \"net/http\"; \"os\"; \"strings\"; \"time\")\nfunc main() {\n  req, err := http.NewRequest(\"GET\", \"https://api.revkeen.com/v2/transactions?type=sale&status=pending\", strings.NewReader(\"\"))\n  if err != nil { panic(err) }\n  req.Header.Set(\"x-api-key\", os.Getenv(\"REVKEEN_API_KEY\"))\n  req.Header.Set(\"Content-Type\", \"application/json\")\n  client := &http.Client{Timeout: 10 * time.Second}\n  response, err := client.Do(req)\n  if err != nil { panic(err) }\n  defer response.Body.Close()\n  if response.StatusCode >= 400 { panic(fmt.Sprintf(\"RevKeen request failed (%d)\", response.StatusCode)) }\n  data, err := io.ReadAll(response.Body)\n  if err != nil { panic(err) }\n  fmt.Println(string(data))\n}"},{"lang":"php","label":"PHP (HTTP)","source":"<?php\n$request = curl_init(\"https://api.revkeen.com/v2/transactions?type=sale&status=pending\");\ncurl_setopt_array($request, [\n  CURLOPT_CUSTOMREQUEST => \"GET\",\n  CURLOPT_RETURNTRANSFER => true,\n  CURLOPT_TIMEOUT => 10,\n  CURLOPT_HTTPHEADER => [\"x-api-key: \" . getenv(\"REVKEEN_API_KEY\"), \"Content-Type: application/json\"],\n]);\n$response = curl_exec($request);\nif ($response === false) throw new RuntimeException(curl_error($request));\n$status = curl_getinfo($request, CURLINFO_HTTP_CODE);\ncurl_close($request);\nif ($status >= 400) throw new RuntimeException(\"RevKeen request failed ($status)\");\necho $response;"},{"lang":"python","label":"Python","source":"import os\n\nimport requests\n\nresponse = requests.get(\n    \"https://api.revkeen.com/v2/transactions\",\n    headers={\"x-api-key\": os.environ[\"REVKEEN_API_KEY\"]},\n    params={\n        \"type\": \"sale\",\n        \"status\": \"pending\",\n    },\n)\n\nresponse.raise_for_status()\nprint(response.json())"},{"lang":"javascript","label":"JavaScript","source":"const credential = process.env.REVKEEN_API_KEY;\nif (!credential) throw new Error(\"Set REVKEEN_API_KEY\");\nconst response = await fetch(\"https://api.revkeen.com/v2/transactions?type=sale&status=pending\", {\n  method: \"GET\",\n  headers: {\n    \"x-api-key\": credential,\n  },\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"java","label":"Java","source":"import java.net.URI;\nimport java.net.http.HttpClient;\nimport java.net.http.HttpRequest;\nimport java.net.http.HttpResponse;\n\nHttpClient client = HttpClient.newHttpClient();\n\nHttpRequest request = HttpRequest.newBuilder()\n    .uri(URI.create(\"https://api.revkeen.com/v2/transactions?type=sale&status=pending\"))\n    .header(\"x-api-key\", System.getenv(\"REVKEEN_API_KEY\"))\n    .GET()\n    .build();\n\nHttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());\nSystem.out.println(response.body());"},{"lang":"ruby","label":"Ruby","source":"require \"net/http\"\nrequire \"json\"\nrequire \"uri\"\n\nuri = URI(\"https://api.revkeen.com/v2/transactions?type=sale&status=pending\")\nhttp = Net::HTTP.new(uri.host, uri.port)\nhttp.use_ssl = true\n\nrequest = Net::HTTP::Get.new(uri)\nrequest[\"x-api-key\"] = ENV[\"REVKEEN_API_KEY\"]\n\nresponse = http.request(request)\nputs response.body"}],"security":[{"apiKey":["payments:read"]},{"oauth2":["payments:read"]}]}},"/transactions/{id}":{"get":{"operationId":"transactions_get","tags":["Transactions"],"x-fern-sdk-group-name":["transactions"],"summary":"Get transaction by ID","description":"Retrieve a single transaction by its ID.\n\n---\n\n**Related endpoints**\n\n- `GET /transactions` — List transactions\n\n**Common errors**\n\n- `401 authentication_error` — missing, invalid, expired, or revoked credential. Codes: `authentication_failed`, `invalid_api_key`, `expired_api_key`, `api_key_revoked`, `session_invalid`, `merchant_required`. Carries a `WWW-Authenticate: Bearer` challenge.\n- `404 resource_missing` — the referenced resource does not exist or is not visible to your key.","parameters":[{"schema":{"type":"string","format":"uuid","description":"Transaction UUID"},"required":true,"description":"Transaction UUID","name":"id","in":"path"}],"responses":{"200":{"description":"Transaction details","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TransactionRetrieveResponse"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}},"headers":{"WWW-Authenticate":{"description":"RFC 6750 Bearer authentication challenge","schema":{"type":"string"},"example":"Bearer realm=\"revkeen-api\""}}},"404":{"description":"Transaction not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}},"required":["error"]}}}},"429":{"description":"Too Many Requests - Rate limit exceeded","headers":{"Retry-After":{"description":"Whole seconds until the caller may retry","schema":{"type":"string"},"example":"60"},"X-RateLimit-Limit":{"description":"Request quota for the current window","schema":{"type":"string"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window","schema":{"type":"string"}},"X-RateLimit-Reset":{"description":"UNIX timestamp in seconds when the window resets","schema":{"type":"string"}}}}},"x-cli-group":"transactions","x-cli-name":"get","x-codeSamples":[{"lang":"curl","label":"cURL","source":"curl -X GET \"https://api.revkeen.com/v2/transactions/11111111-1111-4111-8111-111111111111\" \\\n  -H \"x-api-key: $REVKEEN_API_KEY\""},{"lang":"typescript","label":"TypeScript (SDK)","source":"// npm install @revkeen/sdk@1.20260822.1541\nimport { RevKeenClient } from \"@revkeen/sdk\";\nconst apiKey = process.env.REVKEEN_API_KEY;\nif (!apiKey) throw new Error(\"Set REVKEEN_API_KEY\");\nconst client = new RevKeenClient({ apiKey, baseUrl: \"https://api.revkeen.com/v2\" });\nconst result = await client.transactions.get(\"11111111-1111-4111-8111-111111111111\");\nconsole.log(result);"},{"lang":"go","label":"Go (HTTP)","source":"package main\nimport (\"fmt\"; \"io\"; \"net/http\"; \"os\"; \"strings\"; \"time\")\nfunc main() {\n  req, err := http.NewRequest(\"GET\", \"https://api.revkeen.com/v2/transactions/11111111-1111-4111-8111-111111111111\", strings.NewReader(\"\"))\n  if err != nil { panic(err) }\n  req.Header.Set(\"x-api-key\", os.Getenv(\"REVKEEN_API_KEY\"))\n  req.Header.Set(\"Content-Type\", \"application/json\")\n  client := &http.Client{Timeout: 10 * time.Second}\n  response, err := client.Do(req)\n  if err != nil { panic(err) }\n  defer response.Body.Close()\n  if response.StatusCode >= 400 { panic(fmt.Sprintf(\"RevKeen request failed (%d)\", response.StatusCode)) }\n  data, err := io.ReadAll(response.Body)\n  if err != nil { panic(err) }\n  fmt.Println(string(data))\n}"},{"lang":"php","label":"PHP (HTTP)","source":"<?php\n$request = curl_init(\"https://api.revkeen.com/v2/transactions/11111111-1111-4111-8111-111111111111\");\ncurl_setopt_array($request, [\n  CURLOPT_CUSTOMREQUEST => \"GET\",\n  CURLOPT_RETURNTRANSFER => true,\n  CURLOPT_TIMEOUT => 10,\n  CURLOPT_HTTPHEADER => [\"x-api-key: \" . getenv(\"REVKEEN_API_KEY\"), \"Content-Type: application/json\"],\n]);\n$response = curl_exec($request);\nif ($response === false) throw new RuntimeException(curl_error($request));\n$status = curl_getinfo($request, CURLINFO_HTTP_CODE);\ncurl_close($request);\nif ($status >= 400) throw new RuntimeException(\"RevKeen request failed ($status)\");\necho $response;"},{"lang":"python","label":"Python","source":"import os\n\nimport requests\n\nresponse = requests.get(\n    \"https://api.revkeen.com/v2/transactions/11111111-1111-4111-8111-111111111111\",\n    headers={\"x-api-key\": os.environ[\"REVKEEN_API_KEY\"]},\n)\n\nresponse.raise_for_status()\nprint(response.json())"},{"lang":"javascript","label":"JavaScript","source":"const credential = process.env.REVKEEN_API_KEY;\nif (!credential) throw new Error(\"Set REVKEEN_API_KEY\");\nconst response = await fetch(\"https://api.revkeen.com/v2/transactions/11111111-1111-4111-8111-111111111111\", {\n  method: \"GET\",\n  headers: {\n    \"x-api-key\": credential,\n  },\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"java","label":"Java","source":"import java.net.URI;\nimport java.net.http.HttpClient;\nimport java.net.http.HttpRequest;\nimport java.net.http.HttpResponse;\n\nHttpClient client = HttpClient.newHttpClient();\n\nHttpRequest request = HttpRequest.newBuilder()\n    .uri(URI.create(\"https://api.revkeen.com/v2/transactions/11111111-1111-4111-8111-111111111111\"))\n    .header(\"x-api-key\", System.getenv(\"REVKEEN_API_KEY\"))\n    .GET()\n    .build();\n\nHttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());\nSystem.out.println(response.body());"},{"lang":"ruby","label":"Ruby","source":"require \"net/http\"\nrequire \"json\"\nrequire \"uri\"\n\nuri = URI(\"https://api.revkeen.com/v2/transactions/11111111-1111-4111-8111-111111111111\")\nhttp = Net::HTTP.new(uri.host, uri.port)\nhttp.use_ssl = true\n\nrequest = Net::HTTP::Get.new(uri)\nrequest[\"x-api-key\"] = ENV[\"REVKEEN_API_KEY\"]\n\nresponse = http.request(request)\nputs response.body"}],"security":[{"apiKey":["payments:read"]},{"oauth2":["payments:read"]}]}},"/dd/mandate-requests/{token}":{"get":{"tags":["Direct Debit"],"operationId":"dd_mandate_requests_get","summary":"Look up a pending signed mandate request","description":"Public lookup of a pending signed mandate request by its emailed token. Rate-limited; returns 404 for unknown, consumed, cancelled, or expired tokens.\n\n---\n\n**Related endpoints**\n\n- `POST /dd/validate` — Validate a UK bank account for Direct Debit\n- `POST /dd/preview` — Preview the Direct Debit collection date chain\n\n**Common errors**\n\n- `404 resource_missing` — the referenced resource does not exist or is not visible to your key.\n- `429 rate_limit_error` — code `rate_limit_exceeded`. Back off using the `Retry-After` header (whole seconds); `X-RateLimit-Reset` is a UNIX timestamp in seconds.","security":[],"parameters":[{"schema":{"type":"string","description":"Signed mandate-request token from the emailed link"},"required":true,"description":"Signed mandate-request token from the emailed link","name":"token","in":"path"}],"responses":{"200":{"description":"Mandate request details","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MandateRequestLookupResponse"}}}},"404":{"description":"Mandate request not found"},"429":{"description":"Too Many Requests - Rate limit exceeded","headers":{"Retry-After":{"schema":{"type":"string","description":"Whole seconds until the caller may retry","example":"60"},"required":true,"description":"Whole seconds until the caller may retry"},"X-RateLimit-Limit":{"schema":{"type":"string","description":"Request quota for the current window. Sent only by limiters that publish one."},"required":false,"description":"Request quota for the current window. Sent only by limiters that publish one."},"X-RateLimit-Remaining":{"schema":{"type":"string","description":"Requests remaining in the current window. Sent only by limiters that publish one."},"required":false,"description":"Requests remaining in the current window. Sent only by limiters that publish one."},"X-RateLimit-Reset":{"schema":{"type":"string","description":"UNIX timestamp in seconds when the window resets. Sent only by limiters that publish one."},"required":false,"description":"UNIX timestamp in seconds when the window resets. Sent only by limiters that publish one."}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RateLimitError"}}}}},"x-cli-group":"dd","x-cli-name":"mandate-requests-get","x-codeSamples":[{"lang":"curl","label":"cURL","source":"curl -X GET \"https://api.revkeen.com/v2/dd/mandate-requests/example\" "},{"lang":"typescript","label":"TypeScript (HTTP)","source":"const response = await fetch(\"https://api.revkeen.com/v2/dd/mandate-requests/example\", {\n  method: \"GET\",\n  headers: {\n  },\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"go","label":"Go (HTTP)","source":"package main\nimport (\"fmt\"; \"io\"; \"net/http\"; \"strings\"; \"time\")\nfunc main() {\n  req, err := http.NewRequest(\"GET\", \"https://api.revkeen.com/v2/dd/mandate-requests/example\", strings.NewReader(\"\"))\n  if err != nil { panic(err) }\n  req.Header.Set(\"Content-Type\", \"application/json\")\n  client := &http.Client{Timeout: 10 * time.Second}\n  response, err := client.Do(req)\n  if err != nil { panic(err) }\n  defer response.Body.Close()\n  if response.StatusCode >= 400 { panic(fmt.Sprintf(\"RevKeen request failed (%d)\", response.StatusCode)) }\n  data, err := io.ReadAll(response.Body)\n  if err != nil { panic(err) }\n  fmt.Println(string(data))\n}"},{"lang":"php","label":"PHP (HTTP)","source":"<?php\n$request = curl_init(\"https://api.revkeen.com/v2/dd/mandate-requests/example\");\ncurl_setopt_array($request, [\n  CURLOPT_CUSTOMREQUEST => \"GET\",\n  CURLOPT_RETURNTRANSFER => true,\n  CURLOPT_TIMEOUT => 10,\n]);\n$response = curl_exec($request);\nif ($response === false) throw new RuntimeException(curl_error($request));\n$status = curl_getinfo($request, CURLINFO_HTTP_CODE);\ncurl_close($request);\nif ($status >= 400) throw new RuntimeException(\"RevKeen request failed ($status)\");\necho $response;"},{"lang":"python","label":"Python","source":"import os\n\nimport requests\n\nresponse = requests.get(\n    \"https://api.revkeen.com/v2/dd/mandate-requests/example\",\n)\n\nresponse.raise_for_status()\nprint(response.json())"},{"lang":"javascript","label":"JavaScript","source":"const response = await fetch(\"https://api.revkeen.com/v2/dd/mandate-requests/example\", {\n  method: \"GET\",\n  headers: {\n  },\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"java","label":"Java","source":"import java.net.URI;\nimport java.net.http.HttpClient;\nimport java.net.http.HttpRequest;\nimport java.net.http.HttpResponse;\n\nHttpClient client = HttpClient.newHttpClient();\n\nHttpRequest request = HttpRequest.newBuilder()\n    .uri(URI.create(\"https://api.revkeen.com/v2/dd/mandate-requests/example\"))\n    .GET()\n    .build();\n\nHttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());\nSystem.out.println(response.body());"},{"lang":"ruby","label":"Ruby","source":"require \"net/http\"\nrequire \"json\"\nrequire \"uri\"\n\nuri = URI(\"https://api.revkeen.com/v2/dd/mandate-requests/example\")\nhttp = Net::HTTP.new(uri.host, uri.port)\nhttp.use_ssl = true\n\nrequest = Net::HTTP::Get.new(uri)\n\nresponse = http.request(request)\nputs response.body"}]}},"/dd/validate":{"post":{"tags":["Direct Debit"],"operationId":"dd_validate","summary":"Validate a UK bank account for Direct Debit","description":"Validates UK bank account details (sort code + account number) for Direct Debit eligibility with the bureau.\n\n---\n\n**Related endpoints**\n\n- `GET /dd/mandate-requests/{token}` — Look up a pending signed mandate request\n- `POST /dd/preview` — Preview the Direct Debit collection date chain\n\n**Common errors**\n\n- `400 invalid_request` — malformed payload or failed validation.\n- `401 authentication_error` — missing, invalid, expired, or revoked credential. Codes: `authentication_failed`, `invalid_api_key`, `expired_api_key`, `api_key_revoked`, `session_invalid`, `merchant_required`. Carries a `WWW-Authenticate: Bearer` challenge.\n\n**Idempotency**\n\nPass an `Idempotency-Key` header (UUID v4 recommended) to make retries safe. Keys are valid for 24 hours; see [the idempotency guide](/docs/fundamentals/idempotency).","security":[{"apiKey":["mandates:write"]}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidateBankAccountRequest"}}}},"responses":{"200":{"description":"Validation result","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidateBankAccountResponse"}}}},"400":{"description":"Direct Debit not enabled, or invalid request"},"401":{"description":"Unauthorized - Invalid or missing API key","headers":{"WWW-Authenticate":{"schema":{"type":"string","description":"RFC 6750 Bearer authentication challenge","example":"Bearer realm=\"revkeen-api\""},"required":true,"description":"RFC 6750 Bearer authentication challenge"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthenticationError"}}}},"429":{"description":"Too Many Requests - Rate limit exceeded","headers":{"Retry-After":{"description":"Whole seconds until the caller may retry","schema":{"type":"string"},"example":"60"},"X-RateLimit-Limit":{"description":"Request quota for the current window","schema":{"type":"string"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window","schema":{"type":"string"}},"X-RateLimit-Reset":{"description":"UNIX timestamp in seconds when the window resets","schema":{"type":"string"}}}},"500":{"description":"Validation failed"}},"x-cli-group":"dd","x-cli-name":"validate","x-codeSamples":[{"lang":"curl","label":"cURL","source":"curl -X POST \"https://api.revkeen.com/v2/dd/validate\" \\\n  -H \"x-api-key: $REVKEEN_API_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"sortCode\": \"56-00-03\",\n    \"accountNumber\": \"13354647\"\n  }'"},{"lang":"typescript","label":"TypeScript (SDK)","source":"// npm install @revkeen/sdk@1.20260822.1541\nimport { RevKeenClient } from \"@revkeen/sdk\";\nconst apiKey = process.env.REVKEEN_API_KEY;\nif (!apiKey) throw new Error(\"Set REVKEEN_API_KEY\");\nconst client = new RevKeenClient({ apiKey, baseUrl: \"https://api.revkeen.com/v2\" });\nconst result = await client.dd.validate({\n  \"sortCode\": \"56-00-03\",\n  \"accountNumber\": \"13354647\"\n});\nconsole.log(result);"},{"lang":"go","label":"Go (HTTP)","source":"package main\nimport (\"fmt\"; \"io\"; \"net/http\"; \"os\"; \"strings\"; \"time\")\nfunc main() {\n  req, err := http.NewRequest(\"POST\", \"https://api.revkeen.com/v2/dd/validate\", strings.NewReader(\"{\\\"sortCode\\\":\\\"56-00-03\\\",\\\"accountNumber\\\":\\\"13354647\\\"}\"))\n  if err != nil { panic(err) }\n  req.Header.Set(\"x-api-key\", os.Getenv(\"REVKEEN_API_KEY\"))\n  req.Header.Set(\"Content-Type\", \"application/json\")\n  client := &http.Client{Timeout: 10 * time.Second}\n  response, err := client.Do(req)\n  if err != nil { panic(err) }\n  defer response.Body.Close()\n  if response.StatusCode >= 400 { panic(fmt.Sprintf(\"RevKeen request failed (%d)\", response.StatusCode)) }\n  data, err := io.ReadAll(response.Body)\n  if err != nil { panic(err) }\n  fmt.Println(string(data))\n}"},{"lang":"php","label":"PHP (HTTP)","source":"<?php\n$request = curl_init(\"https://api.revkeen.com/v2/dd/validate\");\ncurl_setopt_array($request, [\n  CURLOPT_CUSTOMREQUEST => \"POST\",\n  CURLOPT_RETURNTRANSFER => true,\n  CURLOPT_TIMEOUT => 10,\n  CURLOPT_HTTPHEADER => [\"x-api-key: \" . getenv(\"REVKEEN_API_KEY\"), \"Content-Type: application/json\"],\n  CURLOPT_POSTFIELDS => '{\\\"sortCode\\\":\\\"56-00-03\\\",\\\"accountNumber\\\":\\\"13354647\\\"}',\n]);\n$response = curl_exec($request);\nif ($response === false) throw new RuntimeException(curl_error($request));\n$status = curl_getinfo($request, CURLINFO_HTTP_CODE);\ncurl_close($request);\nif ($status >= 400) throw new RuntimeException(\"RevKeen request failed ($status)\");\necho $response;"},{"lang":"python","label":"Python","source":"import os\n\nimport requests\n\nresponse = requests.post(\n    \"https://api.revkeen.com/v2/dd/validate\",\n    headers={\"x-api-key\": os.environ[\"REVKEEN_API_KEY\"]},\n    json={\n        \"sortCode\": \"56-00-03\",\n        \"accountNumber\": \"13354647\",\n    },\n)\n\nresponse.raise_for_status()\nprint(response.json())"},{"lang":"javascript","label":"JavaScript","source":"const credential = process.env.REVKEEN_API_KEY;\nif (!credential) throw new Error(\"Set REVKEEN_API_KEY\");\nconst response = await fetch(\"https://api.revkeen.com/v2/dd/validate\", {\n  method: \"POST\",\n  headers: {\n    \"x-api-key\": credential,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n    \"sortCode\": \"56-00-03\",\n    \"accountNumber\": \"13354647\"\n  }),\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"java","label":"Java","source":"import java.net.URI;\nimport java.net.http.HttpClient;\nimport java.net.http.HttpRequest;\nimport java.net.http.HttpResponse;\n\nHttpClient client = HttpClient.newHttpClient();\n\nHttpRequest request = HttpRequest.newBuilder()\n    .uri(URI.create(\"https://api.revkeen.com/v2/dd/validate\"))\n    .header(\"x-api-key\", System.getenv(\"REVKEEN_API_KEY\"))\n    .header(\"Content-Type\", \"application/json\")\n    .method(\"POST\", HttpRequest.BodyPublishers.ofString(\"\"\"\n        {\n          \"sortCode\": \"56-00-03\",\n          \"accountNumber\": \"13354647\"\n        }\n        \"\"\"))\n    .build();\n\nHttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());\nSystem.out.println(response.body());"},{"lang":"ruby","label":"Ruby","source":"require \"net/http\"\nrequire \"json\"\nrequire \"uri\"\n\nuri = URI(\"https://api.revkeen.com/v2/dd/validate\")\nhttp = Net::HTTP.new(uri.host, uri.port)\nhttp.use_ssl = true\n\nrequest = Net::HTTP::Post.new(uri)\nrequest[\"x-api-key\"] = ENV[\"REVKEEN_API_KEY\"]\nrequest[\"Content-Type\"] = \"application/json\"\nrequest.body = {\n  \"sortCode\" => \"56-00-03\",\n  \"accountNumber\" => \"13354647\",\n}.to_json\n\nresponse = http.request(request)\nputs response.body"}]}},"/dd/preview":{"post":{"tags":["Direct Debit"],"operationId":"dd_preview","summary":"Preview the Direct Debit collection date chain","description":"Returns the backend-owned working-day date chain (advance notice → submission → collection → settlement) for a Direct Debit collection. Clients never compute these dates.\n\n---\n\n**Related endpoints**\n\n- `GET /dd/mandate-requests/{token}` — Look up a pending signed mandate request\n- `POST /dd/validate` — Validate a UK bank account for Direct Debit\n\n**Common errors**\n\n- `400 invalid_request` — malformed payload or failed validation.\n- `401 authentication_error` — missing, invalid, expired, or revoked credential. Codes: `authentication_failed`, `invalid_api_key`, `expired_api_key`, `api_key_revoked`, `session_invalid`, `merchant_required`. Carries a `WWW-Authenticate: Bearer` challenge.\n\n**Idempotency**\n\nPass an `Idempotency-Key` header (UUID v4 recommended) to make retries safe. Keys are valid for 24 hours; see [the idempotency guide](/docs/fundamentals/idempotency).","security":[{"apiKey":["mandates:read"]}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PreviewCollectionRequest"}}}},"responses":{"200":{"description":"Collection date chain","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PreviewCollectionResponse"}}}},"400":{"description":"Direct Debit not enabled, or invalid request"},"401":{"description":"Unauthorized - Invalid or missing API key","headers":{"WWW-Authenticate":{"schema":{"type":"string","description":"RFC 6750 Bearer authentication challenge","example":"Bearer realm=\"revkeen-api\""},"required":true,"description":"RFC 6750 Bearer authentication challenge"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthenticationError"}}}},"429":{"description":"Too Many Requests - Rate limit exceeded","headers":{"Retry-After":{"description":"Whole seconds until the caller may retry","schema":{"type":"string"},"example":"60"},"X-RateLimit-Limit":{"description":"Request quota for the current window","schema":{"type":"string"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window","schema":{"type":"string"}},"X-RateLimit-Reset":{"description":"UNIX timestamp in seconds when the window resets","schema":{"type":"string"}}}},"500":{"description":"Preview failed"}},"x-cli-group":"dd","x-cli-name":"preview","x-codeSamples":[{"lang":"curl","label":"cURL","source":"curl -X POST \"https://api.revkeen.com/v2/dd/preview\" \\\n  -H \"x-api-key: $REVKEEN_API_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"requestedCollectionDate\": \"2026-07-01\",\n    \"subscriptionId\": \"11111111-1111-4111-8111-111111111111\"\n  }'"},{"lang":"typescript","label":"TypeScript (SDK)","source":"// npm install @revkeen/sdk@1.20260822.1541\nimport { RevKeenClient } from \"@revkeen/sdk\";\nconst apiKey = process.env.REVKEEN_API_KEY;\nif (!apiKey) throw new Error(\"Set REVKEEN_API_KEY\");\nconst client = new RevKeenClient({ apiKey, baseUrl: \"https://api.revkeen.com/v2\" });\nconst result = await client.dd.preview({\n  \"requestedCollectionDate\": \"2026-07-01\",\n  \"subscriptionId\": \"11111111-1111-4111-8111-111111111111\"\n});\nconsole.log(result);"},{"lang":"go","label":"Go (HTTP)","source":"package main\nimport (\"fmt\"; \"io\"; \"net/http\"; \"os\"; \"strings\"; \"time\")\nfunc main() {\n  req, err := http.NewRequest(\"POST\", \"https://api.revkeen.com/v2/dd/preview\", strings.NewReader(\"{\\\"requestedCollectionDate\\\":\\\"2026-07-01\\\",\\\"subscriptionId\\\":\\\"11111111-1111-4111-8111-111111111111\\\"}\"))\n  if err != nil { panic(err) }\n  req.Header.Set(\"x-api-key\", os.Getenv(\"REVKEEN_API_KEY\"))\n  req.Header.Set(\"Content-Type\", \"application/json\")\n  client := &http.Client{Timeout: 10 * time.Second}\n  response, err := client.Do(req)\n  if err != nil { panic(err) }\n  defer response.Body.Close()\n  if response.StatusCode >= 400 { panic(fmt.Sprintf(\"RevKeen request failed (%d)\", response.StatusCode)) }\n  data, err := io.ReadAll(response.Body)\n  if err != nil { panic(err) }\n  fmt.Println(string(data))\n}"},{"lang":"php","label":"PHP (HTTP)","source":"<?php\n$request = curl_init(\"https://api.revkeen.com/v2/dd/preview\");\ncurl_setopt_array($request, [\n  CURLOPT_CUSTOMREQUEST => \"POST\",\n  CURLOPT_RETURNTRANSFER => true,\n  CURLOPT_TIMEOUT => 10,\n  CURLOPT_HTTPHEADER => [\"x-api-key: \" . getenv(\"REVKEEN_API_KEY\"), \"Content-Type: application/json\"],\n  CURLOPT_POSTFIELDS => '{\\\"requestedCollectionDate\\\":\\\"2026-07-01\\\",\\\"subscriptionId\\\":\\\"11111111-1111-4111-8111-111111111111\\\"}',\n]);\n$response = curl_exec($request);\nif ($response === false) throw new RuntimeException(curl_error($request));\n$status = curl_getinfo($request, CURLINFO_HTTP_CODE);\ncurl_close($request);\nif ($status >= 400) throw new RuntimeException(\"RevKeen request failed ($status)\");\necho $response;"},{"lang":"python","label":"Python","source":"import os\n\nimport requests\n\nresponse = requests.post(\n    \"https://api.revkeen.com/v2/dd/preview\",\n    headers={\"x-api-key\": os.environ[\"REVKEEN_API_KEY\"]},\n    json={\n        \"requestedCollectionDate\": \"2026-07-01\",\n        \"subscriptionId\": \"11111111-1111-4111-8111-111111111111\",\n    },\n)\n\nresponse.raise_for_status()\nprint(response.json())"},{"lang":"javascript","label":"JavaScript","source":"const credential = process.env.REVKEEN_API_KEY;\nif (!credential) throw new Error(\"Set REVKEEN_API_KEY\");\nconst response = await fetch(\"https://api.revkeen.com/v2/dd/preview\", {\n  method: \"POST\",\n  headers: {\n    \"x-api-key\": credential,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n    \"requestedCollectionDate\": \"2026-07-01\",\n    \"subscriptionId\": \"11111111-1111-4111-8111-111111111111\"\n  }),\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"java","label":"Java","source":"import java.net.URI;\nimport java.net.http.HttpClient;\nimport java.net.http.HttpRequest;\nimport java.net.http.HttpResponse;\n\nHttpClient client = HttpClient.newHttpClient();\n\nHttpRequest request = HttpRequest.newBuilder()\n    .uri(URI.create(\"https://api.revkeen.com/v2/dd/preview\"))\n    .header(\"x-api-key\", System.getenv(\"REVKEEN_API_KEY\"))\n    .header(\"Content-Type\", \"application/json\")\n    .method(\"POST\", HttpRequest.BodyPublishers.ofString(\"\"\"\n        {\n          \"requestedCollectionDate\": \"2026-07-01\",\n          \"subscriptionId\": \"11111111-1111-4111-8111-111111111111\"\n        }\n        \"\"\"))\n    .build();\n\nHttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());\nSystem.out.println(response.body());"},{"lang":"ruby","label":"Ruby","source":"require \"net/http\"\nrequire \"json\"\nrequire \"uri\"\n\nuri = URI(\"https://api.revkeen.com/v2/dd/preview\")\nhttp = Net::HTTP.new(uri.host, uri.port)\nhttp.use_ssl = true\n\nrequest = Net::HTTP::Post.new(uri)\nrequest[\"x-api-key\"] = ENV[\"REVKEEN_API_KEY\"]\nrequest[\"Content-Type\"] = \"application/json\"\nrequest.body = {\n  \"requestedCollectionDate\" => \"2026-07-01\",\n  \"subscriptionId\" => \"11111111-1111-4111-8111-111111111111\",\n}.to_json\n\nresponse = http.request(request)\nputs response.body"}]}},"/mandates":{"get":{"tags":["Mandates"],"operationId":"mandates_list","summary":"List Direct Debit mandates","security":[{"apiKey":["mandates:read"]}],"parameters":[{"schema":{"type":"string","format":"uuid","description":"Filter mandates by customer (UUID)"},"required":false,"description":"Filter mandates by customer (UUID)","name":"customerId","in":"query"},{"schema":{"type":"string","enum":["pending_lodgement","active","suspended","cancelled","failed"],"description":"Filter mandates by status"},"required":false,"description":"Filter mandates by status","name":"status","in":"query"}],"responses":{"200":{"description":"List of mandates","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MandateListResponse"}}}},"400":{"description":"A query parameter failed validation; `param` names it","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MandateListValidationError"}}}},"401":{"description":"Unauthorized - Invalid or missing API key","headers":{"WWW-Authenticate":{"schema":{"type":"string","description":"RFC 6750 Bearer authentication challenge","example":"Bearer realm=\"revkeen-api\""},"required":true,"description":"RFC 6750 Bearer authentication challenge"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthenticationError"}}}},"429":{"description":"Too Many Requests - Rate limit exceeded","headers":{"Retry-After":{"description":"Whole seconds until the caller may retry","schema":{"type":"string"},"example":"60"},"X-RateLimit-Limit":{"description":"Request quota for the current window","schema":{"type":"string"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window","schema":{"type":"string"}},"X-RateLimit-Reset":{"description":"UNIX timestamp in seconds when the window resets","schema":{"type":"string"}}}}},"x-cli-group":"mandates","x-cli-name":"list","x-codeSamples":[{"lang":"curl","label":"cURL","source":"curl -X GET \"https://api.revkeen.com/v2/mandates?customerId=11111111-1111-4111-8111-111111111111&status=pending_lodgement\" \\\n  -H \"x-api-key: $REVKEEN_API_KEY\""},{"lang":"typescript","label":"TypeScript (SDK)","source":"// npm install @revkeen/sdk@1.20260822.1541\nimport { RevKeenClient } from \"@revkeen/sdk\";\nconst apiKey = process.env.REVKEEN_API_KEY;\nif (!apiKey) throw new Error(\"Set REVKEEN_API_KEY\");\nconst client = new RevKeenClient({ apiKey, baseUrl: \"https://api.revkeen.com/v2\" });\nconst result = await client.mandates.list({\n  \"customerId\": \"11111111-1111-4111-8111-111111111111\",\n  \"status\": \"pending_lodgement\"\n});\nconsole.log(result);"},{"lang":"go","label":"Go (HTTP)","source":"package main\nimport (\"fmt\"; \"io\"; \"net/http\"; \"os\"; \"strings\"; \"time\")\nfunc main() {\n  req, err := http.NewRequest(\"GET\", \"https://api.revkeen.com/v2/mandates?customerId=11111111-1111-4111-8111-111111111111&status=pending_lodgement\", strings.NewReader(\"\"))\n  if err != nil { panic(err) }\n  req.Header.Set(\"x-api-key\", os.Getenv(\"REVKEEN_API_KEY\"))\n  req.Header.Set(\"Content-Type\", \"application/json\")\n  client := &http.Client{Timeout: 10 * time.Second}\n  response, err := client.Do(req)\n  if err != nil { panic(err) }\n  defer response.Body.Close()\n  if response.StatusCode >= 400 { panic(fmt.Sprintf(\"RevKeen request failed (%d)\", response.StatusCode)) }\n  data, err := io.ReadAll(response.Body)\n  if err != nil { panic(err) }\n  fmt.Println(string(data))\n}"},{"lang":"php","label":"PHP (HTTP)","source":"<?php\n$request = curl_init(\"https://api.revkeen.com/v2/mandates?customerId=11111111-1111-4111-8111-111111111111&status=pending_lodgement\");\ncurl_setopt_array($request, [\n  CURLOPT_CUSTOMREQUEST => \"GET\",\n  CURLOPT_RETURNTRANSFER => true,\n  CURLOPT_TIMEOUT => 10,\n  CURLOPT_HTTPHEADER => [\"x-api-key: \" . getenv(\"REVKEEN_API_KEY\"), \"Content-Type: application/json\"],\n]);\n$response = curl_exec($request);\nif ($response === false) throw new RuntimeException(curl_error($request));\n$status = curl_getinfo($request, CURLINFO_HTTP_CODE);\ncurl_close($request);\nif ($status >= 400) throw new RuntimeException(\"RevKeen request failed ($status)\");\necho $response;"},{"lang":"python","label":"Python","source":"import os\n\nimport requests\n\nresponse = requests.get(\n    \"https://api.revkeen.com/v2/mandates\",\n    headers={\"x-api-key\": os.environ[\"REVKEEN_API_KEY\"]},\n    params={\n        \"customerId\": \"11111111-1111-4111-8111-111111111111\",\n        \"status\": \"pending_lodgement\",\n    },\n)\n\nresponse.raise_for_status()\nprint(response.json())"},{"lang":"javascript","label":"JavaScript","source":"const credential = process.env.REVKEEN_API_KEY;\nif (!credential) throw new Error(\"Set REVKEEN_API_KEY\");\nconst response = await fetch(\"https://api.revkeen.com/v2/mandates?customerId=11111111-1111-4111-8111-111111111111&status=pending_lodgement\", {\n  method: \"GET\",\n  headers: {\n    \"x-api-key\": credential,\n  },\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"java","label":"Java","source":"import java.net.URI;\nimport java.net.http.HttpClient;\nimport java.net.http.HttpRequest;\nimport java.net.http.HttpResponse;\n\nHttpClient client = HttpClient.newHttpClient();\n\nHttpRequest request = HttpRequest.newBuilder()\n    .uri(URI.create(\"https://api.revkeen.com/v2/mandates?customerId=11111111-1111-4111-8111-111111111111&status=pending_lodgement\"))\n    .header(\"x-api-key\", System.getenv(\"REVKEEN_API_KEY\"))\n    .GET()\n    .build();\n\nHttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());\nSystem.out.println(response.body());"},{"lang":"ruby","label":"Ruby","source":"require \"net/http\"\nrequire \"json\"\nrequire \"uri\"\n\nuri = URI(\"https://api.revkeen.com/v2/mandates?customerId=11111111-1111-4111-8111-111111111111&status=pending_lodgement\")\nhttp = Net::HTTP.new(uri.host, uri.port)\nhttp.use_ssl = true\n\nrequest = Net::HTTP::Get.new(uri)\nrequest[\"x-api-key\"] = ENV[\"REVKEEN_API_KEY\"]\n\nresponse = http.request(request)\nputs response.body"}],"description":"---\n\n**Related endpoints**\n\n- `GET /mandates/{id}` — Retrieve a Direct Debit mandate\n- `POST /mandates/{id}/cancel` — Cancel a Direct Debit mandate\n- `POST /mandates/{id}/suspend` — Suspend a Direct Debit mandate\n- `POST /mandates/{id}/reinstate` — Reinstate a suspended Direct Debit mandate\n- `POST /mandates/{id}/collections` — Schedule a one-off Direct Debit collection\n\n**Common errors**\n\n- `400 invalid_request` — malformed payload or failed validation.\n- `401 authentication_error` — missing, invalid, expired, or revoked credential. Codes: `authentication_failed`, `invalid_api_key`, `expired_api_key`, `api_key_revoked`, `session_invalid`, `merchant_required`. Carries a `WWW-Authenticate: Bearer` challenge.\n\n**Pagination**\n\nOffset-based with `limit` (default 25, max 100) and `offset`. The response `pagination` block includes `total` and `hasMore`. See [the pagination guide](/docs/fundamentals/pagination) for SDK auto-paging helpers."}},"/mandates/{id}":{"get":{"tags":["Mandates"],"operationId":"mandates_get","summary":"Retrieve a Direct Debit mandate","security":[{"apiKey":["mandates:read"]}],"parameters":[{"schema":{"type":"string","format":"uuid","description":"Mandate UUID"},"required":true,"description":"Mandate UUID","name":"id","in":"path"}],"responses":{"200":{"description":"Mandate","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MandateResponse"}}}},"400":{"description":"`id` is not a UUID","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MandateRetrieveValidationError"}}}},"401":{"description":"Unauthorized - Invalid or missing API key","headers":{"WWW-Authenticate":{"schema":{"type":"string","description":"RFC 6750 Bearer authentication challenge","example":"Bearer realm=\"revkeen-api\""},"required":true,"description":"RFC 6750 Bearer authentication challenge"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthenticationError"}}}},"404":{"description":"Mandate not found"},"429":{"description":"Too Many Requests - Rate limit exceeded","headers":{"Retry-After":{"description":"Whole seconds until the caller may retry","schema":{"type":"string"},"example":"60"},"X-RateLimit-Limit":{"description":"Request quota for the current window","schema":{"type":"string"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window","schema":{"type":"string"}},"X-RateLimit-Reset":{"description":"UNIX timestamp in seconds when the window resets","schema":{"type":"string"}}}}},"x-cli-group":"mandates","x-cli-name":"get","x-codeSamples":[{"lang":"curl","label":"cURL","source":"curl -X GET \"https://api.revkeen.com/v2/mandates/11111111-1111-4111-8111-111111111111\" \\\n  -H \"x-api-key: $REVKEEN_API_KEY\""},{"lang":"typescript","label":"TypeScript (SDK)","source":"// npm install @revkeen/sdk@1.20260822.1541\nimport { RevKeenClient } from \"@revkeen/sdk\";\nconst apiKey = process.env.REVKEEN_API_KEY;\nif (!apiKey) throw new Error(\"Set REVKEEN_API_KEY\");\nconst client = new RevKeenClient({ apiKey, baseUrl: \"https://api.revkeen.com/v2\" });\nconst result = await client.mandates.get(\"11111111-1111-4111-8111-111111111111\");\nconsole.log(result);"},{"lang":"go","label":"Go (HTTP)","source":"package main\nimport (\"fmt\"; \"io\"; \"net/http\"; \"os\"; \"strings\"; \"time\")\nfunc main() {\n  req, err := http.NewRequest(\"GET\", \"https://api.revkeen.com/v2/mandates/11111111-1111-4111-8111-111111111111\", strings.NewReader(\"\"))\n  if err != nil { panic(err) }\n  req.Header.Set(\"x-api-key\", os.Getenv(\"REVKEEN_API_KEY\"))\n  req.Header.Set(\"Content-Type\", \"application/json\")\n  client := &http.Client{Timeout: 10 * time.Second}\n  response, err := client.Do(req)\n  if err != nil { panic(err) }\n  defer response.Body.Close()\n  if response.StatusCode >= 400 { panic(fmt.Sprintf(\"RevKeen request failed (%d)\", response.StatusCode)) }\n  data, err := io.ReadAll(response.Body)\n  if err != nil { panic(err) }\n  fmt.Println(string(data))\n}"},{"lang":"php","label":"PHP (HTTP)","source":"<?php\n$request = curl_init(\"https://api.revkeen.com/v2/mandates/11111111-1111-4111-8111-111111111111\");\ncurl_setopt_array($request, [\n  CURLOPT_CUSTOMREQUEST => \"GET\",\n  CURLOPT_RETURNTRANSFER => true,\n  CURLOPT_TIMEOUT => 10,\n  CURLOPT_HTTPHEADER => [\"x-api-key: \" . getenv(\"REVKEEN_API_KEY\"), \"Content-Type: application/json\"],\n]);\n$response = curl_exec($request);\nif ($response === false) throw new RuntimeException(curl_error($request));\n$status = curl_getinfo($request, CURLINFO_HTTP_CODE);\ncurl_close($request);\nif ($status >= 400) throw new RuntimeException(\"RevKeen request failed ($status)\");\necho $response;"},{"lang":"python","label":"Python","source":"import os\n\nimport requests\n\nresponse = requests.get(\n    \"https://api.revkeen.com/v2/mandates/11111111-1111-4111-8111-111111111111\",\n    headers={\"x-api-key\": os.environ[\"REVKEEN_API_KEY\"]},\n)\n\nresponse.raise_for_status()\nprint(response.json())"},{"lang":"javascript","label":"JavaScript","source":"const credential = process.env.REVKEEN_API_KEY;\nif (!credential) throw new Error(\"Set REVKEEN_API_KEY\");\nconst response = await fetch(\"https://api.revkeen.com/v2/mandates/11111111-1111-4111-8111-111111111111\", {\n  method: \"GET\",\n  headers: {\n    \"x-api-key\": credential,\n  },\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"java","label":"Java","source":"import java.net.URI;\nimport java.net.http.HttpClient;\nimport java.net.http.HttpRequest;\nimport java.net.http.HttpResponse;\n\nHttpClient client = HttpClient.newHttpClient();\n\nHttpRequest request = HttpRequest.newBuilder()\n    .uri(URI.create(\"https://api.revkeen.com/v2/mandates/11111111-1111-4111-8111-111111111111\"))\n    .header(\"x-api-key\", System.getenv(\"REVKEEN_API_KEY\"))\n    .GET()\n    .build();\n\nHttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());\nSystem.out.println(response.body());"},{"lang":"ruby","label":"Ruby","source":"require \"net/http\"\nrequire \"json\"\nrequire \"uri\"\n\nuri = URI(\"https://api.revkeen.com/v2/mandates/11111111-1111-4111-8111-111111111111\")\nhttp = Net::HTTP.new(uri.host, uri.port)\nhttp.use_ssl = true\n\nrequest = Net::HTTP::Get.new(uri)\nrequest[\"x-api-key\"] = ENV[\"REVKEEN_API_KEY\"]\n\nresponse = http.request(request)\nputs response.body"}],"description":"---\n\n**Related endpoints**\n\n- `GET /mandates` — List Direct Debit mandates\n- `POST /mandates/{id}/cancel` — Cancel a Direct Debit mandate\n- `POST /mandates/{id}/suspend` — Suspend a Direct Debit mandate\n- `POST /mandates/{id}/reinstate` — Reinstate a suspended Direct Debit mandate\n- `POST /mandates/{id}/collections` — Schedule a one-off Direct Debit collection\n\n**Common errors**\n\n- `400 invalid_request` — malformed payload or failed validation.\n- `401 authentication_error` — missing, invalid, expired, or revoked credential. Codes: `authentication_failed`, `invalid_api_key`, `expired_api_key`, `api_key_revoked`, `session_invalid`, `merchant_required`. Carries a `WWW-Authenticate: Bearer` challenge.\n- `404 resource_missing` — the referenced resource does not exist or is not visible to your key."}},"/mandates/{id}/cancel":{"post":{"tags":["Mandates"],"operationId":"mandates_cancel","summary":"Cancel a Direct Debit mandate","description":"Cancels a mandate. If it was lodged with the bureau, the London & Zurich customer is cancelled too.\n\n---\n\n**Related endpoints**\n\n- `GET /mandates` — List Direct Debit mandates\n- `GET /mandates/{id}` — Retrieve a Direct Debit mandate\n- `POST /mandates/{id}/suspend` — Suspend a Direct Debit mandate\n- `POST /mandates/{id}/reinstate` — Reinstate a suspended Direct Debit mandate\n- `POST /mandates/{id}/collections` — Schedule a one-off Direct Debit collection\n\n**Common errors**\n\n- `400 invalid_request` — malformed payload or failed validation.\n- `401 authentication_error` — missing, invalid, expired, or revoked credential. Codes: `authentication_failed`, `invalid_api_key`, `expired_api_key`, `api_key_revoked`, `session_invalid`, `merchant_required`. Carries a `WWW-Authenticate: Bearer` challenge.\n- `404 resource_missing` — the referenced resource does not exist or is not visible to your key.\n\n**Idempotency**\n\nPass an `Idempotency-Key` header (UUID v4 recommended) to make retries safe. Keys are valid for 24 hours; see [the idempotency guide](/docs/fundamentals/idempotency).","security":[{"apiKey":[]}],"parameters":[{"schema":{"type":"string","format":"uuid","description":"Mandate UUID"},"required":true,"description":"Mandate UUID","name":"id","in":"path"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MandateActionRequest"}}}},"responses":{"200":{"description":"Mandate cancelled","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MandateActionResponse"}}}},"400":{"description":"Mandate is already cancelled"},"401":{"description":"Unauthorized - Invalid or missing API key","headers":{"WWW-Authenticate":{"schema":{"type":"string","description":"RFC 6750 Bearer authentication challenge","example":"Bearer realm=\"revkeen-api\""},"required":true,"description":"RFC 6750 Bearer authentication challenge"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthenticationError"}}}},"404":{"description":"Mandate not found"},"429":{"description":"Too Many Requests - Rate limit exceeded","headers":{"Retry-After":{"description":"Whole seconds until the caller may retry","schema":{"type":"string"},"example":"60"},"X-RateLimit-Limit":{"description":"Request quota for the current window","schema":{"type":"string"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window","schema":{"type":"string"}},"X-RateLimit-Reset":{"description":"UNIX timestamp in seconds when the window resets","schema":{"type":"string"}}}}},"x-cli-group":"mandates","x-cli-name":"cancel","x-codeSamples":[{"lang":"curl","label":"cURL","source":"curl -X POST \"https://api.revkeen.com/v2/mandates/11111111-1111-4111-8111-111111111111/cancel\" \\\n  -H \"x-api-key: $REVKEEN_API_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"reason\": \"customer_request\"\n  }'"},{"lang":"typescript","label":"TypeScript (SDK)","source":"// npm install @revkeen/sdk@1.20260822.1541\nimport { RevKeenClient } from \"@revkeen/sdk\";\nconst apiKey = process.env.REVKEEN_API_KEY;\nif (!apiKey) throw new Error(\"Set REVKEEN_API_KEY\");\nconst client = new RevKeenClient({ apiKey, baseUrl: \"https://api.revkeen.com/v2\" });\nconst result = await client.mandates.cancel(\"11111111-1111-4111-8111-111111111111\", {\n  \"reason\": \"customer_request\"\n});\nconsole.log(result);"},{"lang":"go","label":"Go (HTTP)","source":"package main\nimport (\"fmt\"; \"io\"; \"net/http\"; \"os\"; \"strings\"; \"time\")\nfunc main() {\n  req, err := http.NewRequest(\"POST\", \"https://api.revkeen.com/v2/mandates/11111111-1111-4111-8111-111111111111/cancel\", strings.NewReader(\"{\\\"reason\\\":\\\"customer_request\\\"}\"))\n  if err != nil { panic(err) }\n  req.Header.Set(\"x-api-key\", os.Getenv(\"REVKEEN_API_KEY\"))\n  req.Header.Set(\"Content-Type\", \"application/json\")\n  client := &http.Client{Timeout: 10 * time.Second}\n  response, err := client.Do(req)\n  if err != nil { panic(err) }\n  defer response.Body.Close()\n  if response.StatusCode >= 400 { panic(fmt.Sprintf(\"RevKeen request failed (%d)\", response.StatusCode)) }\n  data, err := io.ReadAll(response.Body)\n  if err != nil { panic(err) }\n  fmt.Println(string(data))\n}"},{"lang":"php","label":"PHP (HTTP)","source":"<?php\n$request = curl_init(\"https://api.revkeen.com/v2/mandates/11111111-1111-4111-8111-111111111111/cancel\");\ncurl_setopt_array($request, [\n  CURLOPT_CUSTOMREQUEST => \"POST\",\n  CURLOPT_RETURNTRANSFER => true,\n  CURLOPT_TIMEOUT => 10,\n  CURLOPT_HTTPHEADER => [\"x-api-key: \" . getenv(\"REVKEEN_API_KEY\"), \"Content-Type: application/json\"],\n  CURLOPT_POSTFIELDS => '{\\\"reason\\\":\\\"customer_request\\\"}',\n]);\n$response = curl_exec($request);\nif ($response === false) throw new RuntimeException(curl_error($request));\n$status = curl_getinfo($request, CURLINFO_HTTP_CODE);\ncurl_close($request);\nif ($status >= 400) throw new RuntimeException(\"RevKeen request failed ($status)\");\necho $response;"},{"lang":"python","label":"Python","source":"import os\n\nimport requests\n\nresponse = requests.post(\n    \"https://api.revkeen.com/v2/mandates/11111111-1111-4111-8111-111111111111/cancel\",\n    headers={\"x-api-key\": os.environ[\"REVKEEN_API_KEY\"]},\n    json={\n        \"reason\": \"customer_request\",\n    },\n)\n\nresponse.raise_for_status()\nprint(response.json())"},{"lang":"javascript","label":"JavaScript","source":"const credential = process.env.REVKEEN_API_KEY;\nif (!credential) throw new Error(\"Set REVKEEN_API_KEY\");\nconst response = await fetch(\"https://api.revkeen.com/v2/mandates/11111111-1111-4111-8111-111111111111/cancel\", {\n  method: \"POST\",\n  headers: {\n    \"x-api-key\": credential,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n    \"reason\": \"customer_request\"\n  }),\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"java","label":"Java","source":"import java.net.URI;\nimport java.net.http.HttpClient;\nimport java.net.http.HttpRequest;\nimport java.net.http.HttpResponse;\n\nHttpClient client = HttpClient.newHttpClient();\n\nHttpRequest request = HttpRequest.newBuilder()\n    .uri(URI.create(\"https://api.revkeen.com/v2/mandates/11111111-1111-4111-8111-111111111111/cancel\"))\n    .header(\"x-api-key\", System.getenv(\"REVKEEN_API_KEY\"))\n    .header(\"Content-Type\", \"application/json\")\n    .method(\"POST\", HttpRequest.BodyPublishers.ofString(\"\"\"\n        {\n          \"reason\": \"customer_request\"\n        }\n        \"\"\"))\n    .build();\n\nHttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());\nSystem.out.println(response.body());"},{"lang":"ruby","label":"Ruby","source":"require \"net/http\"\nrequire \"json\"\nrequire \"uri\"\n\nuri = URI(\"https://api.revkeen.com/v2/mandates/11111111-1111-4111-8111-111111111111/cancel\")\nhttp = Net::HTTP.new(uri.host, uri.port)\nhttp.use_ssl = true\n\nrequest = Net::HTTP::Post.new(uri)\nrequest[\"x-api-key\"] = ENV[\"REVKEEN_API_KEY\"]\nrequest[\"Content-Type\"] = \"application/json\"\nrequest.body = {\n  \"reason\" => \"customer_request\",\n}.to_json\n\nresponse = http.request(request)\nputs response.body"}]}},"/mandates/{id}/suspend":{"post":{"tags":["Mandates"],"operationId":"mandates_suspend","summary":"Suspend a Direct Debit mandate","description":"Suspends an active (or pending-lodgement) mandate.\n\n---\n\n**Related endpoints**\n\n- `GET /mandates` — List Direct Debit mandates\n- `GET /mandates/{id}` — Retrieve a Direct Debit mandate\n- `POST /mandates/{id}/cancel` — Cancel a Direct Debit mandate\n- `POST /mandates/{id}/reinstate` — Reinstate a suspended Direct Debit mandate\n- `POST /mandates/{id}/collections` — Schedule a one-off Direct Debit collection\n\n**Common errors**\n\n- `400 invalid_request` — malformed payload or failed validation.\n- `401 authentication_error` — missing, invalid, expired, or revoked credential. Codes: `authentication_failed`, `invalid_api_key`, `expired_api_key`, `api_key_revoked`, `session_invalid`, `merchant_required`. Carries a `WWW-Authenticate: Bearer` challenge.\n- `404 resource_missing` — the referenced resource does not exist or is not visible to your key.\n\n**Idempotency**\n\nPass an `Idempotency-Key` header (UUID v4 recommended) to make retries safe. Keys are valid for 24 hours; see [the idempotency guide](/docs/fundamentals/idempotency).","security":[{"apiKey":[]}],"parameters":[{"schema":{"type":"string","format":"uuid","description":"Mandate UUID"},"required":true,"description":"Mandate UUID","name":"id","in":"path"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MandateActionRequest"}}}},"responses":{"200":{"description":"Mandate suspended","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MandateActionResponse"}}}},"400":{"description":"Mandate cannot be suspended from its current status"},"401":{"description":"Unauthorized - Invalid or missing API key","headers":{"WWW-Authenticate":{"schema":{"type":"string","description":"RFC 6750 Bearer authentication challenge","example":"Bearer realm=\"revkeen-api\""},"required":true,"description":"RFC 6750 Bearer authentication challenge"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthenticationError"}}}},"404":{"description":"Mandate not found"},"429":{"description":"Too Many Requests - Rate limit exceeded","headers":{"Retry-After":{"description":"Whole seconds until the caller may retry","schema":{"type":"string"},"example":"60"},"X-RateLimit-Limit":{"description":"Request quota for the current window","schema":{"type":"string"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window","schema":{"type":"string"}},"X-RateLimit-Reset":{"description":"UNIX timestamp in seconds when the window resets","schema":{"type":"string"}}}}},"x-cli-group":"mandates","x-cli-name":"suspend","x-codeSamples":[{"lang":"curl","label":"cURL","source":"curl -X POST \"https://api.revkeen.com/v2/mandates/11111111-1111-4111-8111-111111111111/suspend\" \\\n  -H \"x-api-key: $REVKEEN_API_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"reason\": \"customer_request\"\n  }'"},{"lang":"typescript","label":"TypeScript (SDK)","source":"// npm install @revkeen/sdk@1.20260822.1541\nimport { RevKeenClient } from \"@revkeen/sdk\";\nconst apiKey = process.env.REVKEEN_API_KEY;\nif (!apiKey) throw new Error(\"Set REVKEEN_API_KEY\");\nconst client = new RevKeenClient({ apiKey, baseUrl: \"https://api.revkeen.com/v2\" });\nconst result = await client.mandates.suspend(\"11111111-1111-4111-8111-111111111111\", {\n  \"reason\": \"customer_request\"\n});\nconsole.log(result);"},{"lang":"go","label":"Go (HTTP)","source":"package main\nimport (\"fmt\"; \"io\"; \"net/http\"; \"os\"; \"strings\"; \"time\")\nfunc main() {\n  req, err := http.NewRequest(\"POST\", \"https://api.revkeen.com/v2/mandates/11111111-1111-4111-8111-111111111111/suspend\", strings.NewReader(\"{\\\"reason\\\":\\\"customer_request\\\"}\"))\n  if err != nil { panic(err) }\n  req.Header.Set(\"x-api-key\", os.Getenv(\"REVKEEN_API_KEY\"))\n  req.Header.Set(\"Content-Type\", \"application/json\")\n  client := &http.Client{Timeout: 10 * time.Second}\n  response, err := client.Do(req)\n  if err != nil { panic(err) }\n  defer response.Body.Close()\n  if response.StatusCode >= 400 { panic(fmt.Sprintf(\"RevKeen request failed (%d)\", response.StatusCode)) }\n  data, err := io.ReadAll(response.Body)\n  if err != nil { panic(err) }\n  fmt.Println(string(data))\n}"},{"lang":"php","label":"PHP (HTTP)","source":"<?php\n$request = curl_init(\"https://api.revkeen.com/v2/mandates/11111111-1111-4111-8111-111111111111/suspend\");\ncurl_setopt_array($request, [\n  CURLOPT_CUSTOMREQUEST => \"POST\",\n  CURLOPT_RETURNTRANSFER => true,\n  CURLOPT_TIMEOUT => 10,\n  CURLOPT_HTTPHEADER => [\"x-api-key: \" . getenv(\"REVKEEN_API_KEY\"), \"Content-Type: application/json\"],\n  CURLOPT_POSTFIELDS => '{\\\"reason\\\":\\\"customer_request\\\"}',\n]);\n$response = curl_exec($request);\nif ($response === false) throw new RuntimeException(curl_error($request));\n$status = curl_getinfo($request, CURLINFO_HTTP_CODE);\ncurl_close($request);\nif ($status >= 400) throw new RuntimeException(\"RevKeen request failed ($status)\");\necho $response;"},{"lang":"python","label":"Python","source":"import os\n\nimport requests\n\nresponse = requests.post(\n    \"https://api.revkeen.com/v2/mandates/11111111-1111-4111-8111-111111111111/suspend\",\n    headers={\"x-api-key\": os.environ[\"REVKEEN_API_KEY\"]},\n    json={\n        \"reason\": \"customer_request\",\n    },\n)\n\nresponse.raise_for_status()\nprint(response.json())"},{"lang":"javascript","label":"JavaScript","source":"const credential = process.env.REVKEEN_API_KEY;\nif (!credential) throw new Error(\"Set REVKEEN_API_KEY\");\nconst response = await fetch(\"https://api.revkeen.com/v2/mandates/11111111-1111-4111-8111-111111111111/suspend\", {\n  method: \"POST\",\n  headers: {\n    \"x-api-key\": credential,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n    \"reason\": \"customer_request\"\n  }),\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"java","label":"Java","source":"import java.net.URI;\nimport java.net.http.HttpClient;\nimport java.net.http.HttpRequest;\nimport java.net.http.HttpResponse;\n\nHttpClient client = HttpClient.newHttpClient();\n\nHttpRequest request = HttpRequest.newBuilder()\n    .uri(URI.create(\"https://api.revkeen.com/v2/mandates/11111111-1111-4111-8111-111111111111/suspend\"))\n    .header(\"x-api-key\", System.getenv(\"REVKEEN_API_KEY\"))\n    .header(\"Content-Type\", \"application/json\")\n    .method(\"POST\", HttpRequest.BodyPublishers.ofString(\"\"\"\n        {\n          \"reason\": \"customer_request\"\n        }\n        \"\"\"))\n    .build();\n\nHttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());\nSystem.out.println(response.body());"},{"lang":"ruby","label":"Ruby","source":"require \"net/http\"\nrequire \"json\"\nrequire \"uri\"\n\nuri = URI(\"https://api.revkeen.com/v2/mandates/11111111-1111-4111-8111-111111111111/suspend\")\nhttp = Net::HTTP.new(uri.host, uri.port)\nhttp.use_ssl = true\n\nrequest = Net::HTTP::Post.new(uri)\nrequest[\"x-api-key\"] = ENV[\"REVKEEN_API_KEY\"]\nrequest[\"Content-Type\"] = \"application/json\"\nrequest.body = {\n  \"reason\" => \"customer_request\",\n}.to_json\n\nresponse = http.request(request)\nputs response.body"}]}},"/mandates/{id}/reinstate":{"post":{"tags":["Mandates"],"operationId":"mandates_reinstate","summary":"Reinstate a suspended Direct Debit mandate","description":"Reinstates a suspended mandate. This is NOT a status flip — it re-lodges the mandate to Bacs (a new AUDDIS instruction) using the securely stored bank details, then moves the mandate to pending_lodgement (REV-3123).\n\n---\n\n**Related endpoints**\n\n- `GET /mandates` — List Direct Debit mandates\n- `GET /mandates/{id}` — Retrieve a Direct Debit mandate\n- `POST /mandates/{id}/cancel` — Cancel a Direct Debit mandate\n- `POST /mandates/{id}/suspend` — Suspend a Direct Debit mandate\n- `POST /mandates/{id}/collections` — Schedule a one-off Direct Debit collection\n\n**Common errors**\n\n- `400 invalid_request` — malformed payload or failed validation.\n- `401 authentication_error` — missing, invalid, expired, or revoked credential. Codes: `authentication_failed`, `invalid_api_key`, `expired_api_key`, `api_key_revoked`, `session_invalid`, `merchant_required`. Carries a `WWW-Authenticate: Bearer` challenge.\n- `404 resource_missing` — the referenced resource does not exist or is not visible to your key.\n- `409 conflict` — Idempotency-Key collision with a different body, or a concurrent state-transition conflict.\n- `422 unprocessable_entity` — business-rule failure (for example, refunding more than the original charge).\n\n**Idempotency**\n\nPass an `Idempotency-Key` header (UUID v4 recommended) to make retries safe. Keys are valid for 24 hours; see [the idempotency guide](/docs/fundamentals/idempotency).","security":[{"apiKey":[]}],"parameters":[{"schema":{"type":"string","format":"uuid","description":"Mandate UUID"},"required":true,"description":"Mandate UUID","name":"id","in":"path"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MandateActionRequest"}}}},"responses":{"200":{"description":"Mandate re-lodged (pending_lodgement)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MandateActionResponse"}}}},"400":{"description":"Mandate is not suspended, or Direct Debit not enabled"},"401":{"description":"Unauthorized - Invalid or missing API key","headers":{"WWW-Authenticate":{"schema":{"type":"string","description":"RFC 6750 Bearer authentication challenge","example":"Bearer realm=\"revkeen-api\""},"required":true,"description":"RFC 6750 Bearer authentication challenge"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthenticationError"}}}},"404":{"description":"Mandate or customer not found"},"409":{"description":"Stored bank details unavailable/invalid, or missing bureau reference"},"422":{"description":"Customer record missing fields required for Bacs"},"429":{"description":"Too Many Requests - Rate limit exceeded","headers":{"Retry-After":{"description":"Whole seconds until the caller may retry","schema":{"type":"string"},"example":"60"},"X-RateLimit-Limit":{"description":"Request quota for the current window","schema":{"type":"string"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window","schema":{"type":"string"}},"X-RateLimit-Reset":{"description":"UNIX timestamp in seconds when the window resets","schema":{"type":"string"}}}},"502":{"description":"Bureau reinstatement failed"},"503":{"description":"Secure credential storage unavailable"}},"x-cli-group":"mandates","x-cli-name":"reinstate","x-codeSamples":[{"lang":"curl","label":"cURL","source":"curl -X POST \"https://api.revkeen.com/v2/mandates/11111111-1111-4111-8111-111111111111/reinstate\" \\\n  -H \"x-api-key: $REVKEEN_API_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"reason\": \"customer_request\"\n  }'"},{"lang":"typescript","label":"TypeScript (SDK)","source":"// npm install @revkeen/sdk@1.20260822.1541\nimport { RevKeenClient } from \"@revkeen/sdk\";\nconst apiKey = process.env.REVKEEN_API_KEY;\nif (!apiKey) throw new Error(\"Set REVKEEN_API_KEY\");\nconst client = new RevKeenClient({ apiKey, baseUrl: \"https://api.revkeen.com/v2\" });\nconst result = await client.mandates.reinstate(\"11111111-1111-4111-8111-111111111111\", {\n  \"reason\": \"customer_request\"\n});\nconsole.log(result);"},{"lang":"go","label":"Go (HTTP)","source":"package main\nimport (\"fmt\"; \"io\"; \"net/http\"; \"os\"; \"strings\"; \"time\")\nfunc main() {\n  req, err := http.NewRequest(\"POST\", \"https://api.revkeen.com/v2/mandates/11111111-1111-4111-8111-111111111111/reinstate\", strings.NewReader(\"{\\\"reason\\\":\\\"customer_request\\\"}\"))\n  if err != nil { panic(err) }\n  req.Header.Set(\"x-api-key\", os.Getenv(\"REVKEEN_API_KEY\"))\n  req.Header.Set(\"Content-Type\", \"application/json\")\n  client := &http.Client{Timeout: 10 * time.Second}\n  response, err := client.Do(req)\n  if err != nil { panic(err) }\n  defer response.Body.Close()\n  if response.StatusCode >= 400 { panic(fmt.Sprintf(\"RevKeen request failed (%d)\", response.StatusCode)) }\n  data, err := io.ReadAll(response.Body)\n  if err != nil { panic(err) }\n  fmt.Println(string(data))\n}"},{"lang":"php","label":"PHP (HTTP)","source":"<?php\n$request = curl_init(\"https://api.revkeen.com/v2/mandates/11111111-1111-4111-8111-111111111111/reinstate\");\ncurl_setopt_array($request, [\n  CURLOPT_CUSTOMREQUEST => \"POST\",\n  CURLOPT_RETURNTRANSFER => true,\n  CURLOPT_TIMEOUT => 10,\n  CURLOPT_HTTPHEADER => [\"x-api-key: \" . getenv(\"REVKEEN_API_KEY\"), \"Content-Type: application/json\"],\n  CURLOPT_POSTFIELDS => '{\\\"reason\\\":\\\"customer_request\\\"}',\n]);\n$response = curl_exec($request);\nif ($response === false) throw new RuntimeException(curl_error($request));\n$status = curl_getinfo($request, CURLINFO_HTTP_CODE);\ncurl_close($request);\nif ($status >= 400) throw new RuntimeException(\"RevKeen request failed ($status)\");\necho $response;"},{"lang":"python","label":"Python","source":"import os\n\nimport requests\n\nresponse = requests.post(\n    \"https://api.revkeen.com/v2/mandates/11111111-1111-4111-8111-111111111111/reinstate\",\n    headers={\"x-api-key\": os.environ[\"REVKEEN_API_KEY\"]},\n    json={\n        \"reason\": \"customer_request\",\n    },\n)\n\nresponse.raise_for_status()\nprint(response.json())"},{"lang":"javascript","label":"JavaScript","source":"const credential = process.env.REVKEEN_API_KEY;\nif (!credential) throw new Error(\"Set REVKEEN_API_KEY\");\nconst response = await fetch(\"https://api.revkeen.com/v2/mandates/11111111-1111-4111-8111-111111111111/reinstate\", {\n  method: \"POST\",\n  headers: {\n    \"x-api-key\": credential,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n    \"reason\": \"customer_request\"\n  }),\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"java","label":"Java","source":"import java.net.URI;\nimport java.net.http.HttpClient;\nimport java.net.http.HttpRequest;\nimport java.net.http.HttpResponse;\n\nHttpClient client = HttpClient.newHttpClient();\n\nHttpRequest request = HttpRequest.newBuilder()\n    .uri(URI.create(\"https://api.revkeen.com/v2/mandates/11111111-1111-4111-8111-111111111111/reinstate\"))\n    .header(\"x-api-key\", System.getenv(\"REVKEEN_API_KEY\"))\n    .header(\"Content-Type\", \"application/json\")\n    .method(\"POST\", HttpRequest.BodyPublishers.ofString(\"\"\"\n        {\n          \"reason\": \"customer_request\"\n        }\n        \"\"\"))\n    .build();\n\nHttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());\nSystem.out.println(response.body());"},{"lang":"ruby","label":"Ruby","source":"require \"net/http\"\nrequire \"json\"\nrequire \"uri\"\n\nuri = URI(\"https://api.revkeen.com/v2/mandates/11111111-1111-4111-8111-111111111111/reinstate\")\nhttp = Net::HTTP.new(uri.host, uri.port)\nhttp.use_ssl = true\n\nrequest = Net::HTTP::Post.new(uri)\nrequest[\"x-api-key\"] = ENV[\"REVKEEN_API_KEY\"]\nrequest[\"Content-Type\"] = \"application/json\"\nrequest.body = {\n  \"reason\" => \"customer_request\",\n}.to_json\n\nresponse = http.request(request)\nputs response.body"}]}},"/mandates/{id}/collections":{"post":{"tags":["Mandates"],"operationId":"mandates_schedule_collection","summary":"Schedule a one-off Direct Debit collection","description":"Schedules a one-off Bacs collection against an active mandate for an invoice. L&Z confirms the permitted collection date, the payer receives the configured regulatory advance notice first, and nothing is marked paid until the bureau confirms collection. Idempotent per source: retrying with the same sourceId returns or reconciles the same provider schedule instead of collecting twice. An invoice source must be issued and still owe money: a draft or pending_approval invoice is refused, and so are paid, void and other closed invoices (409 invoice_not_collectable at the final check before bureau submission).\n\n---\n\n**Related endpoints**\n\n- `GET /mandates` — List Direct Debit mandates\n- `GET /mandates/{id}` — Retrieve a Direct Debit mandate\n- `POST /mandates/{id}/cancel` — Cancel a Direct Debit mandate\n- `POST /mandates/{id}/suspend` — Suspend a Direct Debit mandate\n- `POST /mandates/{id}/reinstate` — Reinstate a suspended Direct Debit mandate\n\n**Common errors**\n\n- `400 invalid_request` — malformed payload or failed validation.\n- `401 authentication_error` — missing, invalid, expired, or revoked credential. Codes: `authentication_failed`, `invalid_api_key`, `expired_api_key`, `api_key_revoked`, `session_invalid`, `merchant_required`. Carries a `WWW-Authenticate: Bearer` challenge.\n- `403 authorization_error` — the caller is identified but the action is denied. Codes include `insufficient_permissions`, `ip_not_allowed`, `merchant_mismatch`, and `origin_not_allowed`.\n- `404 resource_missing` — the referenced resource does not exist or is not visible to your key.\n- `409 conflict` — Idempotency-Key collision with a different body, or a concurrent state-transition conflict.\n- `422 unprocessable_entity` — business-rule failure (for example, refunding more than the original charge).\n\n**Idempotency**\n\nPass an `Idempotency-Key` header (UUID v4 recommended) to make retries safe. Keys are valid for 24 hours; see [the idempotency guide](/docs/fundamentals/idempotency).","security":[{"apiKey":["mandates:write"]}],"parameters":[{"schema":{"type":"string","format":"uuid","description":"Mandate UUID"},"required":true,"description":"Mandate UUID","name":"id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ScheduleCollectionRequest"}}}},"responses":{"200":{"description":"An existing schedule for this source was returned (idempotent retry)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ScheduleCollectionResponse"}}}},"201":{"description":"Collection scheduled","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ScheduleCollectionResponse"}}}},"400":{"description":"Invalid request"},"401":{"description":"Unauthorized - Invalid or missing API key","headers":{"WWW-Authenticate":{"schema":{"type":"string","description":"RFC 6750 Bearer authentication challenge","example":"Bearer realm=\"revkeen-api\""},"required":true,"description":"RFC 6750 Bearer authentication challenge"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthenticationError"}}}},"403":{"description":"Forbidden - Insufficient permissions","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ForbiddenError"}}}},"404":{"description":"Mandate not found"},"409":{"description":"Mandate is not active, a collection for this source is already in flight, or the invoice is not collectable (draft, pending approval, paid, void or otherwise closed)"},"422":{"description":"Direct Debit not enabled, non-GBP currency, or the collection timeline is not eligible"},"429":{"description":"Too Many Requests - Rate limit exceeded","headers":{"Retry-After":{"description":"Whole seconds until the caller may retry","schema":{"type":"string"},"example":"60"},"X-RateLimit-Limit":{"description":"Request quota for the current window","schema":{"type":"string"}},"X-RateLimit-Remaining":{"description":"Requests remaining in the current window","schema":{"type":"string"}},"X-RateLimit-Reset":{"description":"UNIX timestamp in seconds when the window resets","schema":{"type":"string"}}}},"502":{"description":"Bureau submission or advance-notice dispatch failed"}},"x-cli-group":"mandates","x-cli-name":"schedule-collection","x-codeSamples":[{"lang":"curl","label":"cURL","source":"curl -X POST \"https://api.revkeen.com/v2/mandates/11111111-1111-4111-8111-111111111111/collections\" \\\n  -H \"x-api-key: $REVKEEN_API_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"amountMinor\": 2500,\n    \"currency\": \"GBP\",\n    \"sourceType\": \"invoice\",\n    \"sourceId\": \"11111111-1111-4111-8111-111111111111\"\n  }'"},{"lang":"typescript","label":"TypeScript (SDK)","source":"// npm install @revkeen/sdk@1.20260822.1541\nimport { RevKeenClient } from \"@revkeen/sdk\";\nconst apiKey = process.env.REVKEEN_API_KEY;\nif (!apiKey) throw new Error(\"Set REVKEEN_API_KEY\");\nconst client = new RevKeenClient({ apiKey, baseUrl: \"https://api.revkeen.com/v2\" });\nconst result = await client.mandates.scheduleCollection(\"11111111-1111-4111-8111-111111111111\", {\n  \"amountMinor\": 2500,\n  \"currency\": \"GBP\",\n  \"sourceType\": \"invoice\",\n  \"sourceId\": \"11111111-1111-4111-8111-111111111111\"\n});\nconsole.log(result);"},{"lang":"go","label":"Go (HTTP)","source":"package main\nimport (\"fmt\"; \"io\"; \"net/http\"; \"os\"; \"strings\"; \"time\")\nfunc main() {\n  req, err := http.NewRequest(\"POST\", \"https://api.revkeen.com/v2/mandates/11111111-1111-4111-8111-111111111111/collections\", strings.NewReader(\"{\\\"amountMinor\\\":2500,\\\"currency\\\":\\\"GBP\\\",\\\"sourceType\\\":\\\"invoice\\\",\\\"sourceId\\\":\\\"11111111-1111-4111-8111-111111111111\\\"}\"))\n  if err != nil { panic(err) }\n  req.Header.Set(\"x-api-key\", os.Getenv(\"REVKEEN_API_KEY\"))\n  req.Header.Set(\"Content-Type\", \"application/json\")\n  client := &http.Client{Timeout: 10 * time.Second}\n  response, err := client.Do(req)\n  if err != nil { panic(err) }\n  defer response.Body.Close()\n  if response.StatusCode >= 400 { panic(fmt.Sprintf(\"RevKeen request failed (%d)\", response.StatusCode)) }\n  data, err := io.ReadAll(response.Body)\n  if err != nil { panic(err) }\n  fmt.Println(string(data))\n}"},{"lang":"php","label":"PHP (HTTP)","source":"<?php\n$request = curl_init(\"https://api.revkeen.com/v2/mandates/11111111-1111-4111-8111-111111111111/collections\");\ncurl_setopt_array($request, [\n  CURLOPT_CUSTOMREQUEST => \"POST\",\n  CURLOPT_RETURNTRANSFER => true,\n  CURLOPT_TIMEOUT => 10,\n  CURLOPT_HTTPHEADER => [\"x-api-key: \" . getenv(\"REVKEEN_API_KEY\"), \"Content-Type: application/json\"],\n  CURLOPT_POSTFIELDS => '{\\\"amountMinor\\\":2500,\\\"currency\\\":\\\"GBP\\\",\\\"sourceType\\\":\\\"invoice\\\",\\\"sourceId\\\":\\\"11111111-1111-4111-8111-111111111111\\\"}',\n]);\n$response = curl_exec($request);\nif ($response === false) throw new RuntimeException(curl_error($request));\n$status = curl_getinfo($request, CURLINFO_HTTP_CODE);\ncurl_close($request);\nif ($status >= 400) throw new RuntimeException(\"RevKeen request failed ($status)\");\necho $response;"},{"lang":"python","label":"Python","source":"import os\n\nimport requests\n\nresponse = requests.post(\n    \"https://api.revkeen.com/v2/mandates/11111111-1111-4111-8111-111111111111/collections\",\n    headers={\"x-api-key\": os.environ[\"REVKEEN_API_KEY\"]},\n    json={\n        \"amountMinor\": 2500,\n        \"currency\": \"GBP\",\n        \"sourceType\": \"invoice\",\n        \"sourceId\": \"11111111-1111-4111-8111-111111111111\",\n    },\n)\n\nresponse.raise_for_status()\nprint(response.json())"},{"lang":"javascript","label":"JavaScript","source":"const credential = process.env.REVKEEN_API_KEY;\nif (!credential) throw new Error(\"Set REVKEEN_API_KEY\");\nconst response = await fetch(\"https://api.revkeen.com/v2/mandates/11111111-1111-4111-8111-111111111111/collections\", {\n  method: \"POST\",\n  headers: {\n    \"x-api-key\": credential,\n    \"Content-Type\": \"application/json\",\n  },\n  body: JSON.stringify({\n    \"amountMinor\": 2500,\n    \"currency\": \"GBP\",\n    \"sourceType\": \"invoice\",\n    \"sourceId\": \"11111111-1111-4111-8111-111111111111\"\n  }),\n});\n\nif (!response.ok) throw new Error(`RevKeen request failed (${response.status})`);\nconst data = await response.json();\nconsole.log(data);"},{"lang":"java","label":"Java","source":"import java.net.URI;\nimport java.net.http.HttpClient;\nimport java.net.http.HttpRequest;\nimport java.net.http.HttpResponse;\n\nHttpClient client = HttpClient.newHttpClient();\n\nHttpRequest request = HttpRequest.newBuilder()\n    .uri(URI.create(\"https://api.revkeen.com/v2/mandates/11111111-1111-4111-8111-111111111111/collections\"))\n    .header(\"x-api-key\", System.getenv(\"REVKEEN_API_KEY\"))\n    .header(\"Content-Type\", \"application/json\")\n    .method(\"POST\", HttpRequest.BodyPublishers.ofString(\"\"\"\n        {\n          \"amountMinor\": 2500,\n          \"currency\": \"GBP\",\n          \"sourceType\": \"invoice\",\n          \"sourceId\": \"11111111-1111-4111-8111-111111111111\"\n        }\n        \"\"\"))\n    .build();\n\nHttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());\nSystem.out.println(response.body());"},{"lang":"ruby","label":"Ruby","source":"require \"net/http\"\nrequire \"json\"\nrequire \"uri\"\n\nuri = URI(\"https://api.revkeen.com/v2/mandates/11111111-1111-4111-8111-111111111111/collections\")\nhttp = Net::HTTP.new(uri.host, uri.port)\nhttp.use_ssl = true\n\nrequest = Net::HTTP::Post.new(uri)\nrequest[\"x-api-key\"] = ENV[\"REVKEEN_API_KEY\"]\nrequest[\"Content-Type\"] = \"application/json\"\nrequest.body = {\n  \"amountMinor\" => 2500,\n  \"currency\" => \"GBP\",\n  \"sourceType\" => \"invoice\",\n  \"sourceId\" => \"11111111-1111-4111-8111-111111111111\",\n}.to_json\n\nresponse = http.request(request)\nputs response.body"}]}}}}