# Create Benefit Source: https://docs.macropay.ai/api-reference/benefits/create post /v1/benefits/ Create a benefit. **Scopes**: `benefits:write` # Delete Benefit Source: https://docs.macropay.ai/api-reference/benefits/delete delete /v1/benefits/{id} Delete a benefit. > [!WARNING] > Every grants associated with the benefit will be revoked. > Users will lose access to the benefit. **Scopes**: `benefits:write` # Get Benefit Source: https://docs.macropay.ai/api-reference/benefits/get get /v1/benefits/{id} Get a benefit by ID. **Scopes**: `benefits:read` `benefits:write` # List Benefits Source: https://docs.macropay.ai/api-reference/benefits/list get /v1/benefits/ List benefits. **Scopes**: `benefits:read` `benefits:write` # List Benefit Grants Source: https://docs.macropay.ai/api-reference/benefits/list-grants get /v1/benefits/{id}/grants List the individual grants for a benefit. It's especially useful to check if a user has been granted a benefit. **Scopes**: `benefits:read` `benefits:write` # Update Benefit Source: https://docs.macropay.ai/api-reference/benefits/update patch /v1/benefits/{id} Update a benefit. **Scopes**: `benefits:write` # Create Checkout Link Source: https://docs.macropay.ai/api-reference/checkout-links/create post /v1/checkout-links/ Create a checkout link. **Scopes**: `checkout_links:write` Looking to create a single use checkout session? Checkout Links are probably **not** what you're looking for. Checkout Links are shareable links that generate checkout sessions when opened. They are very handy to start a purchase from your website or social media. However, if you want to start a checkout for one of your user **inside** your product, you should use the [Checkout Sessions API](/api-reference/checkouts/create-session). # Delete Checkout Link Source: https://docs.macropay.ai/api-reference/checkout-links/delete delete /v1/checkout-links/{id} Delete a checkout link. **Scopes**: `checkout_links:write` # Get Checkout Link Source: https://docs.macropay.ai/api-reference/checkout-links/get get /v1/checkout-links/{id} Get a checkout link by ID. **Scopes**: `checkout_links:read` `checkout_links:write` # List Checkout Links Source: https://docs.macropay.ai/api-reference/checkout-links/list get /v1/checkout-links/ List checkout links. **Scopes**: `checkout_links:read` `checkout_links:write` # Update Checkout Link Source: https://docs.macropay.ai/api-reference/checkout-links/update patch /v1/checkout-links/{id} Update a checkout link. **Scopes**: `checkout_links:write` # Confirm Checkout Session from Client Source: https://docs.macropay.ai/api-reference/checkouts/confirm-session-from-client post /v1/checkouts/client/{client_secret}/confirm Confirm a checkout session by client secret. Orders and subscriptions will be processed. # Create Checkout Session Source: https://docs.macropay.ai/api-reference/checkouts/create-session post /v1/checkouts/ Create a checkout session. **Scopes**: `checkouts:write` # Get Checkout Session Source: https://docs.macropay.ai/api-reference/checkouts/get-session get /v1/checkouts/{id} Get a checkout session by ID. **Scopes**: `checkouts:read` `checkouts:write` # Get Checkout Session from Client Source: https://docs.macropay.ai/api-reference/checkouts/get-session-from-client get /v1/checkouts/client/{client_secret} Get a checkout session by client secret. # List Checkout Sessions Source: https://docs.macropay.ai/api-reference/checkouts/list-sessions get /v1/checkouts/ List checkout sessions. **Scopes**: `checkouts:read` `checkouts:write` # Update Checkout Session Source: https://docs.macropay.ai/api-reference/checkouts/update-session patch /v1/checkouts/{id} Update a checkout session. **Scopes**: `checkouts:write` # Update Checkout Session from Client Source: https://docs.macropay.ai/api-reference/checkouts/update-session-from-client patch /v1/checkouts/client/{client_secret} Update a checkout session by client secret. # Create Custom Field Source: https://docs.macropay.ai/api-reference/custom-fields/create post /v1/custom-fields/ Create a custom field. **Scopes**: `custom_fields:write` # Delete Custom Field Source: https://docs.macropay.ai/api-reference/custom-fields/delete delete /v1/custom-fields/{id} Delete a custom field. **Scopes**: `custom_fields:write` # Get Custom Field Source: https://docs.macropay.ai/api-reference/custom-fields/get get /v1/custom-fields/{id} Get a custom field by ID. **Scopes**: `custom_fields:read` `custom_fields:write` # List Custom Fields Source: https://docs.macropay.ai/api-reference/custom-fields/list get /v1/custom-fields/ List custom fields. **Scopes**: `custom_fields:read` `custom_fields:write` # Update Custom Field Source: https://docs.macropay.ai/api-reference/custom-fields/update patch /v1/custom-fields/{id} Update a custom field. **Scopes**: `custom_fields:write` # Get Customer Meter Source: https://docs.macropay.ai/api-reference/customer-meters/get get /v1/customer-meters/{id} Get a customer meter by ID. **Scopes**: `customer_meters:read` # List Customer Meters Source: https://docs.macropay.ai/api-reference/customer-meters/list get /v1/customer-meters/ List customer meters. **Scopes**: `customer_meters:read` # Get Source: https://docs.macropay.ai/api-reference/customer-portal/downloadables/get get /v1/customer-portal/downloadables/{token} # List Downloadables Source: https://docs.macropay.ai/api-reference/customer-portal/downloadables/list get /v1/customer-portal/downloadables/ **Scopes**: `customer_portal:read` `customer_portal:write` # Get Customer Source: https://docs.macropay.ai/api-reference/customer-portal/get-customer get /v1/customer-portal/customers/me Get authenticated customer. **Scopes**: `customer_portal:read` `customer_portal:write` # Get Organization Source: https://docs.macropay.ai/api-reference/customer-portal/get-organization get /v1/customer-portal/organizations/{slug} Get a customer portal's organization by slug. # Activate License Key Source: https://docs.macropay.ai/api-reference/customer-portal/license-keys/activate post /v1/customer-portal/license-keys/activate Activate a license key instance. > This endpoint doesn't require authentication and can be safely used on a public > client, like a desktop application or a mobile app. > If you plan to validate a license key on a server, use the `/v1/license-keys/activate` > endpoint instead. You only need to use this endpoint if you have device **activations** enabled on the license key benefit. You then use this endpoint to reserve an allocation for a specific device. Storing the unique activation ID from the response on the device and using it as extra validation in the [/validate](/api-reference/customer-portal/license-keys/validate) endpoint. Not using **activations**? Just use the [/validate](/api-reference/customer-portal/license-keys/validate) endpoint directly instead. # Deactivate License Key Source: https://docs.macropay.ai/api-reference/customer-portal/license-keys/deactivate post /v1/customer-portal/license-keys/deactivate Deactivate a license key instance. > This endpoint doesn't require authentication and can be safely used on a public > client, like a desktop application or a mobile app. > If you plan to validate a license key on a server, use the `/v1/license-keys/deactivate` > endpoint instead. # Get License Key Source: https://docs.macropay.ai/api-reference/customer-portal/license-keys/get get /v1/customer-portal/license-keys/{id} Get a license key. **Scopes**: `customer_portal:read` `customer_portal:write` # List License Keys Source: https://docs.macropay.ai/api-reference/customer-portal/license-keys/list get /v1/customer-portal/license-keys/ **Scopes**: `customer_portal:read` `customer_portal:write` # Validate License Key Source: https://docs.macropay.ai/api-reference/customer-portal/license-keys/validate post /v1/customer-portal/license-keys/validate Validate a license key. > This endpoint doesn't require authentication and can be safely used on a public > client, like a desktop application or a mobile app. > If you plan to validate a license key on a server, use the `/v1/license-keys/validate` > endpoint instead. # Get Order Source: https://docs.macropay.ai/api-reference/customer-portal/orders/get get /v1/customer-portal/orders/{id} Get an order by ID for the authenticated customer. # Get Order Invoice Source: https://docs.macropay.ai/api-reference/customer-portal/orders/get-invoice get /v1/customer-portal/orders/{id}/invoice Get an order's invoice data. The invoice must be generated first before it can be retrieved. You should call the [`POST /v1/customer-portal/orders/{id}/invoice`](/api-reference/customer-portal/orders/post-invoice) endpoint to generate the invoice. If the invoice is not generated, you will receive a `404` error. # List Orders Source: https://docs.macropay.ai/api-reference/customer-portal/orders/list get /v1/customer-portal/orders/ List orders of the authenticated customer. # Update Order Source: https://docs.macropay.ai/api-reference/customer-portal/orders/patch patch /v1/customer-portal/orders/{id} Update an order for the authenticated customer. # Generate Order Invoice Source: https://docs.macropay.ai/api-reference/customer-portal/orders/post-invoice post /v1/customer-portal/orders/{id}/invoice Trigger generation of an order's invoice. Once the invoice is generated, it's permanent and cannot be modified. Make sure the billing details (name and address) are correct before generating the invoice. You can update them before generating the invoice by calling the [`PATCH /v1/customer-portal/orders/{id}`](/api-reference/customer-portal/orders/patch) endpoint. After successfully calling this endpoint, you get a `202` response, meaning the generation of the invoice has been scheduled. It usually only takes a few seconds before you can retrieve the invoice using the [`GET /v1/customer-portal/orders/{id}/invoice`](/api-reference/customer-portal/orders/get-invoice) endpoint. If you want a reliable notification when the invoice is ready, you can listen to the [`order.updated`](/api-reference/webhooks/order.updated) webhook and check the [`is_invoice_generated` field](/api-reference/webhooks/order.updated#schema-data-is-invoice-generated). # Assign Seat Source: https://docs.macropay.ai/api-reference/customer-portal/seats/assign post /v1/customer-portal/seats # List Seats Source: https://docs.macropay.ai/api-reference/customer-portal/seats/list get /v1/customer-portal/seats **Scopes**: `customer_portal:read` `customer_portal:write` # List Claimed Subscriptions Source: https://docs.macropay.ai/api-reference/customer-portal/seats/list-subscriptions get /v1/customer-portal/seats/subscriptions List all subscriptions where the authenticated customer has claimed a seat. **Scopes**: `customer_portal:read` `customer_portal:write` # Resend Invitation Source: https://docs.macropay.ai/api-reference/customer-portal/seats/resend post /v1/customer-portal/seats/{seat_id}/resend # Revoke Seat Source: https://docs.macropay.ai/api-reference/customer-portal/seats/revoke delete /v1/customer-portal/seats/{seat_id} # Create Customer Session Source: https://docs.macropay.ai/api-reference/customer-portal/sessions/create post /v1/customer-sessions/ Create a customer session. For organizations with `member_model_enabled`, this will automatically create a member session for the owner member of the customer. **Scopes**: `customer_sessions:write` # Cancel Subscription Source: https://docs.macropay.ai/api-reference/customer-portal/subscriptions/cancel delete /v1/customer-portal/subscriptions/{id} Cancel a subscription of the authenticated customer. # Get Subscription Source: https://docs.macropay.ai/api-reference/customer-portal/subscriptions/get get /v1/customer-portal/subscriptions/{id} Get a subscription for the authenticated customer. **Scopes**: `customer_portal:read` `customer_portal:write` # List Subscriptions Source: https://docs.macropay.ai/api-reference/customer-portal/subscriptions/list get /v1/customer-portal/subscriptions/ List subscriptions of the authenticated customer. **Scopes**: `customer_portal:read` `customer_portal:write` # Update Subscription Source: https://docs.macropay.ai/api-reference/customer-portal/subscriptions/update patch /v1/customer-portal/subscriptions/{id} Update a subscription of the authenticated customer. # Assign Seat Source: https://docs.macropay.ai/api-reference/customer-seats/assign post /v1/customer-seats **Scopes**: `customer_seats:write` # Claim Seat Source: https://docs.macropay.ai/api-reference/customer-seats/claim post /v1/customer-seats/claim # Get Claim Info Source: https://docs.macropay.ai/api-reference/customer-seats/get-claim-info get /v1/customer-seats/claim/{invitation_token} # List Seats Source: https://docs.macropay.ai/api-reference/customer-seats/list get /v1/customer-seats **Scopes**: `customer_seats:write` # Resend Invitation Source: https://docs.macropay.ai/api-reference/customer-seats/resend post /v1/customer-seats/{seat_id}/resend **Scopes**: `customer_seats:write` # Revoke Seat Source: https://docs.macropay.ai/api-reference/customer-seats/revoke delete /v1/customer-seats/{seat_id} **Scopes**: `customer_seats:write` # Create Customer Source: https://docs.macropay.ai/api-reference/customers/create post /v1/customers/ Create a customer. **Scopes**: `customers:write` # Delete Customer Source: https://docs.macropay.ai/api-reference/customers/delete delete /v1/customers/{id} Delete a customer. This action cannot be undone and will immediately: - Cancel any active subscriptions for the customer - Revoke all their benefits - Clear any `external_id` Use it only in the context of deleting a user within your own service. Otherwise, use more granular API endpoints to cancel a specific subscription or revoke certain benefits. Note: The customers information will nonetheless be retained for historic orders and subscriptions. Set `anonymize=true` to also anonymize PII for GDPR compliance. **Scopes**: `customers:write` # Delete Customer by External ID Source: https://docs.macropay.ai/api-reference/customers/delete-external delete /v1/customers/external/{external_id} Delete a customer by external ID. Immediately cancels any active subscriptions and revokes any active benefits. Set `anonymize=true` to also anonymize PII for GDPR compliance. **Scopes**: `customers:write` # Get Customer Source: https://docs.macropay.ai/api-reference/customers/get get /v1/customers/{id} Get a customer by ID. **Scopes**: `customers:read` `customers:write` # Get Customer by External ID Source: https://docs.macropay.ai/api-reference/customers/get-external get /v1/customers/external/{external_id} Get a customer by external ID. **Scopes**: `customers:read` `customers:write` # List Customers Source: https://docs.macropay.ai/api-reference/customers/list get /v1/customers/ List customers. **Scopes**: `customers:read` `customers:write` # Get Customer State Source: https://docs.macropay.ai/api-reference/customers/state get /v1/customers/{id}/state Get a customer state by ID. The customer state includes information about the customer's active subscriptions and benefits. It's the ideal endpoint to use when you need to get a full overview of a customer's status. **Scopes**: `customers:read` `customers:write` # Get Customer State by External ID Source: https://docs.macropay.ai/api-reference/customers/state-external get /v1/customers/external/{external_id}/state Get a customer state by external ID. The customer state includes information about the customer's active subscriptions and benefits. It's the ideal endpoint to use when you need to get a full overview of a customer's status. **Scopes**: `customers:read` `customers:write` # Update Customer Source: https://docs.macropay.ai/api-reference/customers/update patch /v1/customers/{id} Update a customer. **Scopes**: `customers:write` # Update Customer by External ID Source: https://docs.macropay.ai/api-reference/customers/update-external patch /v1/customers/external/{external_id} Update a customer by external ID. **Scopes**: `customers:write` # Create Discount Source: https://docs.macropay.ai/api-reference/discounts/create post /v1/discounts/ Create a discount. **Scopes**: `discounts:write` # Delete Discount Source: https://docs.macropay.ai/api-reference/discounts/delete delete /v1/discounts/{id} Delete a discount. **Scopes**: `discounts:write` # Get Discount Source: https://docs.macropay.ai/api-reference/discounts/get get /v1/discounts/{id} Get a discount by ID. **Scopes**: `discounts:read` `discounts:write` # List Discounts Source: https://docs.macropay.ai/api-reference/discounts/list get /v1/discounts/ List discounts. **Scopes**: `discounts:read` `discounts:write` # Update Discount Source: https://docs.macropay.ai/api-reference/discounts/update patch /v1/discounts/{id} Update a discount. **Scopes**: `discounts:write` # Get Event Source: https://docs.macropay.ai/api-reference/events/get get /v1/events/{id} Get an event by ID. **Scopes**: `events:read` `events:write` # Ingest Events Source: https://docs.macropay.ai/api-reference/events/ingest post /v1/events/ingest Ingest batch of events. **Scopes**: `events:write` # List Events Source: https://docs.macropay.ai/api-reference/events/list get /v1/events/ List events. **Scopes**: `events:read` `events:write` # Complete File Upload Source: https://docs.macropay.ai/api-reference/files/complete-upload post /v1/files/{id}/uploaded Complete a file upload. **Scopes**: `files:write` # Create File Source: https://docs.macropay.ai/api-reference/files/create post /v1/files/ Create a file. **Scopes**: `files:write` # Delete File Source: https://docs.macropay.ai/api-reference/files/delete delete /v1/files/{id} Delete a file. **Scopes**: `files:write` # List Files Source: https://docs.macropay.ai/api-reference/files/list get /v1/files/ List files. **Scopes**: `files:read` `files:write` # Update File Source: https://docs.macropay.ai/api-reference/files/update patch /v1/files/{id} Update a file. **Scopes**: `files:write` # API Overview Source: https://docs.macropay.ai/api-reference/introduction Base URLs, authentication, pagination, rate limits, error handling, idempotency, and the difference between the Core API and the Customer Portal API `https://api.macropay.ai/v1` `https://sandbox-api.macropay.ai/v1` Use an **Organization Access Token (OAT)** in the `Authorization: Bearer` header Use a **Customer Access Token** created via `/v1/customer-sessions/` ## Base URLs | Environment | Base URL | Purpose | | ----------- | ------------------------------------ | ------------------------------- | | Production | `https://api.macropay.ai/v1` | Real customers & live payments | | Sandbox | `https://sandbox-api.macropay.ai/v1` | Safe testing & integration work | The sandbox environment is fully isolated—data, users, tokens, and organizations created there do not affect production. Create separate tokens in each environment. Read more: [Sandbox Environment](/integrate/sandbox) ## Authentication ### Organization Access Tokens (OAT) Use an **OAT** to act on behalf of your organization (manage products, prices, checkouts, orders, subscriptions, benefits, etc.). ```http theme={null} Authorization: Bearer macropay_oat_xxxxxxxxxxxxxxxxx ``` Create OATs in your organization settings. See: [Organization Access Tokens](/integrate/oat) Never expose an OAT in client-side code, public repos, or logs. If leaked, it will be revoked automatically by our secret scanning integrations. ### Customer Access Tokens Do **not** use OATs in the browser. For customer-facing flows, [generate a **Customer Session**](/api-reference/customer-portal/sessions/create) server-side, then use the returned **customer access token** with the **Customer Portal API** to let a signed-in customer view their own orders, subscriptions, and benefits. ## Core API vs Customer Portal API | Aspect | Core API | Customer Portal API | | -------------------- | ------------------------------------------------------------------------ | ---------------------------------------------- | | Audience | Your server / backend | One of your customer | | Auth Type | Organization Access Token (OAT) | Customer Access Token | | Scope | Full org resources (products, orders, subscriptions, benefits, checkout) | Only the authenticated customer's data | | Typical Use | Admin dashboards, internal tools, automation, provisioning | Building a custom customer portal or gated app | | Token Creation | Via dashboard (manual) | Via `/v1/customer-sessions/` (server-side) | | Sensitive Operations | Yes (create/update products, issue refunds, etc.) | No (read/update only what the customer owns) | The Customer Portal API is a *restricted* surface designed for safe exposure in user-facing contexts (after exchanging a session). It cannot perform privileged org-level mutations like creating products or issuing refunds. ## Quick Examples ```bash curl (Production - Core API) theme={null} curl https://api.macropay.ai/v1/products/ \ -H "Authorization: Bearer $MACROPAY_OAT" \ -H "Accept: application/json" ``` ```bash curl (Sandbox - Core API) theme={null} curl https://sandbox-api.macropay.ai/v1/products/ \ -H "Authorization: Bearer $MACROPAY_OAT_SANDBOX" \ -H "Accept: application/json" ``` ```bash curl (Customer Portal API) theme={null} curl https://api.macropay.ai/v1/customer-portal/orders/ \ -H "Authorization: Bearer $MACROPAY_CUSTOMER_TOKEN" \ -H "Accept: application/json" ``` ## Using SDKs All official SDKs accept a `server` parameter for sandbox usage: ```ts TypeScript theme={null} import { Macropay } from "@macropayments/sdk"; const macropay = new Macropay({ accessToken: process.env.MACROPAY_ACCESS_TOKEN!, server: "sandbox", // omit or use 'production' for live }); ``` ```py Python theme={null} from macropay import Macropay client = Macropay( access_token=os.environ["MACROPAY_ACCESS_TOKEN"], server="sandbox", ) ``` ```go Go theme={null} s := macropaygo.New( macropaygo.WithServer("sandbox"), macropaygo.WithSecurity(os.Getenv("MACROPAY_ACCESS_TOKEN")), ) ``` ```php PHP theme={null} $sdk = Macropay\Macropay::builder() ->setServer('sandbox') ->setSecurity(getenv('MACROPAY_ACCESS_TOKEN')) ->build(); ``` ## Error Responses All API errors follow a consistent format based on [RFC 9457 (Problem Details)](https://www.rfc-editor.org/rfc/rfc9457): ```json theme={null} { "type": "urn:macropay:error:resource-not-found", "title": "Resource Not Found", "detail": "The requested resource could not be found.", "status": 404, "request_id": "req_abc123...", "error": "ResourceNotFound" } ``` | Field | Type | Description | | ------------ | ------- | --------------------------------------------------------------- | | `type` | string | A stable URN identifying the error class | | `title` | string | A human-readable summary of the error | | `detail` | string | A more specific explanation of what went wrong | | `status` | integer | The HTTP status code | | `request_id` | string | A unique identifier for the request (useful for support) | | `error` | string | The error class name (e.g., `ResourceNotFound`, `NotPermitted`) | ### Common Error Codes | Status | Error | Description | | ------ | ------------------------ | -------------------------------------------- | | `400` | `BadRequest` | The request body or parameters are invalid | | `401` | `Unauthorized` | Missing or invalid authentication token | | `403` | `NotPermitted` | The token does not have the required scope | | `404` | `ResourceNotFound` | The requested resource does not exist | | `409` | `Conflict` | Resource is locked or in a conflicting state | | `422` | `RequestValidationError` | Request body failed schema validation | | `429` | `TooManyRequests` | Rate limit exceeded | ### Validation Errors For `422` responses, the `detail` field contains an array of validation errors with the field path, error message, and error type: ```json theme={null} { "type": "urn:macropay:error:request-validation-error", "title": "Request Validation Error", "detail": [ { "loc": ["body", "name"], "msg": "Field required", "type": "missing" } ], "status": 422, "request_id": "req_abc123..." } ``` ## Idempotency Keys For `POST` endpoints that create resources, you can include an `X-Idempotency-Key` header to safely retry requests without creating duplicate resources: ```http theme={null} POST /v1/checkouts/ HTTP/1.1 Authorization: Bearer macropay_oat_xxx X-Idempotency-Key: order_abc123_checkout Content-Type: application/json ``` If a request with the same idempotency key has already been processed, the API will return the original response without creating a new resource. Keys are scoped to your organization and expire after 24 hours. Use idempotency keys for any create operation that could be retried due to network failures, timeouts, or other transient errors. ## Pagination List endpoints in the Macropay API support pagination to help you efficiently retrieve large datasets. Use the `page` and `limit` query parameters to control pagination. ### Query Parameters | Parameter | Type | Default | Max | Description | | --------- | ------- | ------- | ----- | ------------------------------------------------ | | `page` | integer | `1` | - | Page number, starting from 1 | | `limit` | integer | `10` | `100` | Number of items to return per page (window size) | The `page` parameter works as a window offset. For example, `page=2&limit=10` means the API will skip the first 10 elements and return the next 10. ### Response Format All paginated responses include a `pagination` object with metadata about the current page and total results: | Field | Type | Description | | ------------- | ------- | ---------------------------------------------------------------- | | `total_count` | integer | Total number of items matching your query across all pages | | `max_page` | integer | Total number of pages available, given the current `limit` value | ### Example Let's say you want to fetch products with a limit of 100 items per page: ```bash Request theme={null} curl https://api.macropay.ai/v1/products/?page=1&limit=100 \ -H "Authorization: Bearer $MACROPAY_OAT" \ -H "Accept: application/json" ``` ```json Response theme={null} { "items": [ { "id": "...", "name": "Product 1", ... }, ... ], "pagination": { "total_count": 250, "max_page": 3 } } ``` In this example: * `total_count=250` indicates there are 250 total products * `limit=100` means each page contains up to 100 products * `max_page=3` means you need to make 3 requests to retrieve all products (pages 1, 2, and 3) To retrieve all pages, increment the `page` parameter from `1` to `max_page`. Our SDKs provide built-in pagination helpers to automatically iterate through all pages. ## Rate Limits Macropay API has rate limits to ensure fair usage and maintain performance. The limits are as follows: * **300 requests per minute** per organization/customer or OAuth2 Client. * **3 requests per second** for unauthenticated license key [validation](/api-reference/customer-portal/license-keys/validate), [activation](/api-reference/customer-portal/license-keys/activate), and [deactivation](/api-reference/customer-portal/license-keys/deactivate) endpoints. If you exceed the rate limit, you will receive a `429 Too Many Requests` response. The response will include a `Retry-After` header indicating how long you should wait before making another request. Organizations requiring higher rate limits for production workloads may contact our support team to discuss elevated limits. # Activate License Key Source: https://docs.macropay.ai/api-reference/license-keys/activate post /v1/license-keys/activate Activate a license key instance. **Scopes**: `license_keys:write` # Deactivate License Key Source: https://docs.macropay.ai/api-reference/license-keys/deactivate post /v1/license-keys/deactivate Deactivate a license key instance. **Scopes**: `license_keys:write` # Get License Key Source: https://docs.macropay.ai/api-reference/license-keys/get get /v1/license-keys/{id} Get a license key. **Scopes**: `license_keys:read` `license_keys:write` # Get Activation Source: https://docs.macropay.ai/api-reference/license-keys/get-activation get /v1/license-keys/{id}/activations/{activation_id} Get a license key activation. **Scopes**: `license_keys:read` `license_keys:write` # List License Keys Source: https://docs.macropay.ai/api-reference/license-keys/list get /v1/license-keys/ Get license keys connected to the given organization & filters. **Scopes**: `license_keys:read` `license_keys:write` # Update License Key Source: https://docs.macropay.ai/api-reference/license-keys/update patch /v1/license-keys/{id} Update a license key. **Scopes**: `license_keys:write` # Validate License Key Source: https://docs.macropay.ai/api-reference/license-keys/validate post /v1/license-keys/validate Validate a license key. **Scopes**: `license_keys:write` # Create Meter Source: https://docs.macropay.ai/api-reference/meters/create post /v1/meters/ Create a meter. **Scopes**: `meters:write` # Get Meter Source: https://docs.macropay.ai/api-reference/meters/get get /v1/meters/{id} Get a meter by ID. **Scopes**: `meters:read` `meters:write` # Get Meter Quantities Source: https://docs.macropay.ai/api-reference/meters/get-quantities get /v1/meters/{id}/quantities Get quantities of a meter over a time period. **Scopes**: `meters:read` `meters:write` # List Meters Source: https://docs.macropay.ai/api-reference/meters/list get /v1/meters/ List meters. **Scopes**: `meters:read` `meters:write` # Update Meter Source: https://docs.macropay.ai/api-reference/meters/update patch /v1/meters/{id} Update a meter. **Scopes**: `meters:write` # Get Metrics Source: https://docs.macropay.ai/api-reference/metrics/get get /v1/metrics/ Get metrics about your orders and subscriptions. Currency values are output in cents. **Scopes**: `metrics:read` # Get Metrics Limits Source: https://docs.macropay.ai/api-reference/metrics/get-limits get /v1/metrics/limits Get the interval limits for the metrics endpoint. **Scopes**: `metrics:read` # Authorize Source: https://docs.macropay.ai/api-reference/oauth2/connect/authorize get /v1/oauth2/authorize # Get User Info Source: https://docs.macropay.ai/api-reference/oauth2/connect/get-user-info get /v1/oauth2/userinfo Get information about the authenticated user. # Introspect Token Source: https://docs.macropay.ai/api-reference/oauth2/connect/introspect-token post /v1/oauth2/introspect Get information about an access token. # Request Token Source: https://docs.macropay.ai/api-reference/oauth2/connect/request-token post /v1/oauth2/token Request an access token using a valid grant. # Revoke Token Source: https://docs.macropay.ai/api-reference/oauth2/connect/revoke-token post /v1/oauth2/revoke Revoke an access token or a refresh token. # Get Order Source: https://docs.macropay.ai/api-reference/orders/get get /v1/orders/{id} Get an order by ID. **Scopes**: `orders:read` # Get Order Invoice Source: https://docs.macropay.ai/api-reference/orders/get-invoice get /v1/orders/{id}/invoice Get an order's invoice data. **Scopes**: `orders:read` The invoice must be generated first before it can be retrieved. You should call the [`POST /v1/orders/{id}/invoice`](/api-reference/orders/post-invoice) endpoint to generate the invoice. If the invoice is not generated, you will receive a `404` error. # List Orders Source: https://docs.macropay.ai/api-reference/orders/list get /v1/orders/ List orders. **Scopes**: `orders:read` # Update Order Source: https://docs.macropay.ai/api-reference/orders/patch patch /v1/orders/{id} Update an order. **Scopes**: `orders:write` # Generate Order Invoice Source: https://docs.macropay.ai/api-reference/orders/post-invoice post /v1/orders/{id}/invoice Trigger generation of an order's invoice. **Scopes**: `orders:read` Once the invoice is generated, it's permanent and cannot be modified. Make sure the billing details (name and address) are correct before generating the invoice. You can update them before generating the invoice by calling the [`PATCH /v1/orders/{id}`](/api-reference/orders/patch) endpoint. After successfully calling this endpoint, you get a `202` response, meaning the generation of the invoice has been scheduled. It usually only takes a few seconds before you can retrieve the invoice using the [`GET /v1/orders/{id} /invoice`](/api-reference/orders/get-invoice) endpoint. If you want a reliable notification when the invoice is ready, you can listen to the [`order.updated`](/api-reference/webhooks/order.updated) webhook and check the [`is_invoice_generated` field](/api-reference/webhooks/order.updated#schema-data-is-invoice-generated). # Create Organization Source: https://docs.macropay.ai/api-reference/organizations/create post /v1/organizations/ Create an organization. **Scopes**: `organizations:write` # Get Organization Source: https://docs.macropay.ai/api-reference/organizations/get GET /v1/organizations/{id} Get an organization by ID. **Scopes**: `organizations:read` `organizations:write` # List Organizations Source: https://docs.macropay.ai/api-reference/organizations/list get /v1/organizations/ List organizations. **Scopes**: `organizations:read` `organizations:write` # Update Organization Source: https://docs.macropay.ai/api-reference/organizations/update patch /v1/organizations/{id} Update an organization. **Scopes**: `organizations:write` # Create Product Source: https://docs.macropay.ai/api-reference/products/create post /v1/products/ Create a product. **Scopes**: `products:write` # Get Product Source: https://docs.macropay.ai/api-reference/products/get get /v1/products/{id} Get a product by ID. **Scopes**: `products:read` `products:write` # List Products Source: https://docs.macropay.ai/api-reference/products/list get /v1/products/ List products. **Scopes**: `products:read` `products:write` # Update Product Source: https://docs.macropay.ai/api-reference/products/update patch /v1/products/{id} Update a product. **Scopes**: `products:write` # Update Product Benefits Source: https://docs.macropay.ai/api-reference/products/update-benefits post /v1/products/{id}/benefits Update benefits granted by a product. **Scopes**: `products:write` # Create Refund Source: https://docs.macropay.ai/api-reference/refunds/create post /v1/refunds/ Create a refund. **Scopes**: `refunds:write` # List Refunds Source: https://docs.macropay.ai/api-reference/refunds/list get /v1/refunds/ List refunds. **Scopes**: `refunds:read` `refunds:write` # Create Subscription Source: https://docs.macropay.ai/api-reference/subscriptions/create post /v1/subscriptions/ Create a subscription programmatically. This endpoint only allows to create subscription on free products. For paid products, use the checkout flow. No initial order will be created and no confirmation email will be sent. **Scopes**: `subscriptions:write` # Get Subscription Source: https://docs.macropay.ai/api-reference/subscriptions/get get /v1/subscriptions/{id} Get a subscription by ID. **Scopes**: `subscriptions:read` `subscriptions:write` # List Subscriptions Source: https://docs.macropay.ai/api-reference/subscriptions/list get /v1/subscriptions/ List subscriptions. **Scopes**: `subscriptions:read` `subscriptions:write` # Revoke Subscription Source: https://docs.macropay.ai/api-reference/subscriptions/revoke delete /v1/subscriptions/{id} Revoke a subscription, i.e cancel immediately. **Scopes**: `subscriptions:write` # Update Subscription Source: https://docs.macropay.ai/api-reference/subscriptions/update patch /v1/subscriptions/{id} Update a subscription. **Scopes**: `subscriptions:write` # benefit.created Source: https://docs.macropay.ai/api-reference/webhooks/benefit.created # benefit.updated Source: https://docs.macropay.ai/api-reference/webhooks/benefit.updated # benefit_grant.created Source: https://docs.macropay.ai/api-reference/webhooks/benefit_grant.created # benefit_grant.cycled Source: https://docs.macropay.ai/api-reference/webhooks/benefit_grant.cycled # benefit_grant.revoked Source: https://docs.macropay.ai/api-reference/webhooks/benefit_grant.revoked # benefit_grant.updated Source: https://docs.macropay.ai/api-reference/webhooks/benefit_grant.updated # checkout.created Source: https://docs.macropay.ai/api-reference/webhooks/checkout.created # checkout.expired Source: https://docs.macropay.ai/api-reference/webhooks/checkout.expired # checkout.updated Source: https://docs.macropay.ai/api-reference/webhooks/checkout.updated # customer.created Source: https://docs.macropay.ai/api-reference/webhooks/customer.created # customer.deleted Source: https://docs.macropay.ai/api-reference/webhooks/customer.deleted # customer.state_changed Source: https://docs.macropay.ai/api-reference/webhooks/customer.state_changed # customer.updated Source: https://docs.macropay.ai/api-reference/webhooks/customer.updated # customer_seat.assigned Source: https://docs.macropay.ai/api-reference/webhooks/customer_seat.assigned # customer_seat.claimed Source: https://docs.macropay.ai/api-reference/webhooks/customer_seat.claimed # customer_seat.revoked Source: https://docs.macropay.ai/api-reference/webhooks/customer_seat.revoked # Create Webhook Endpoint Source: https://docs.macropay.ai/api-reference/webhooks/endpoints/create post /v1/webhooks/endpoints Create a webhook endpoint. **Scopes**: `webhooks:write` # Delete Webhook Endpoint Source: https://docs.macropay.ai/api-reference/webhooks/endpoints/delete delete /v1/webhooks/endpoints/{id} Delete a webhook endpoint. **Scopes**: `webhooks:write` # Get Webhook Endpoint Source: https://docs.macropay.ai/api-reference/webhooks/endpoints/get get /v1/webhooks/endpoints/{id} Get a webhook endpoint by ID. **Scopes**: `webhooks:read` `webhooks:write` # List Webhook Endpoints Source: https://docs.macropay.ai/api-reference/webhooks/endpoints/list get /v1/webhooks/endpoints List webhook endpoints. **Scopes**: `webhooks:read` `webhooks:write` # Update Webhook Endpoint Source: https://docs.macropay.ai/api-reference/webhooks/endpoints/update patch /v1/webhooks/endpoints/{id} Update a webhook endpoint. **Scopes**: `webhooks:write` # member.created Source: https://docs.macropay.ai/api-reference/webhooks/member.created # member.deleted Source: https://docs.macropay.ai/api-reference/webhooks/member.deleted # member.updated Source: https://docs.macropay.ai/api-reference/webhooks/member.updated # order.created Source: https://docs.macropay.ai/api-reference/webhooks/order.created # order.paid Source: https://docs.macropay.ai/api-reference/webhooks/order.paid # order.refunded Source: https://docs.macropay.ai/api-reference/webhooks/order.refunded # order.updated Source: https://docs.macropay.ai/api-reference/webhooks/order.updated # organization.updated Source: https://docs.macropay.ai/api-reference/webhooks/organization.updated # product.created Source: https://docs.macropay.ai/api-reference/webhooks/product.created # product.updated Source: https://docs.macropay.ai/api-reference/webhooks/product.updated # refund.created Source: https://docs.macropay.ai/api-reference/webhooks/refund.created # refund.updated Source: https://docs.macropay.ai/api-reference/webhooks/refund.updated # subscription.active Source: https://docs.macropay.ai/api-reference/webhooks/subscription.active # subscription.canceled Source: https://docs.macropay.ai/api-reference/webhooks/subscription.canceled # subscription.created Source: https://docs.macropay.ai/api-reference/webhooks/subscription.created # subscription.past_due Source: https://docs.macropay.ai/api-reference/webhooks/subscription.past_due # subscription.revoked Source: https://docs.macropay.ai/api-reference/webhooks/subscription.revoked # subscription.uncanceled Source: https://docs.macropay.ai/api-reference/webhooks/subscription.uncanceled # subscription.updated Source: https://docs.macropay.ai/api-reference/webhooks/subscription.updated # How to Bill for LLM Inference: The Complete Guide Source: https://docs.macropay.ai/blog/ai-inference-billing-guide Meter tokens, protect your margin, and collect payment for AI inference — with global tax handled for you. DIY metering vs. managed billing, compared. The hard part of charging for AI isn't the model call — it's everything downstream. A request can burn 200 tokens or 200,000. Each provider reports usage with a different schema. Cached tokens are cheaper than fresh ones. And underneath the price you quote a customer sits a cost you pay a model provider that shifts every quarter. Get the gap between those two numbers wrong and you either bleed money on power users or scare off everyone else. This guide compares the two realistic paths — build your own metering stack, or run it on Macropay — and then shows how to set margin, included credits, and overage so your AI product actually makes money. Macropay is a **Merchant of Record**. When a customer pays for your AI product, we are the legal seller, so we calculate and remit **sales tax and VAT worldwide**, keep you off the hook for that liability, and stay PCI DSS Level 1 compliant. You ship the product; we handle the money, the tax, and the compliance. ## What "billing for inference" actually requires Before picking an approach, it helps to see the full surface area. Charging for LLM usage means owning all of this: | Layer | Job | | ------------- | -------------------------------------------------------------------------------- | | **Capture** | Record tokens, model, and cost on every request | | **Normalize** | Reconcile `prompt_tokens` / `input_tokens` / `promptTokenCount` across providers | | **Aggregate** | Roll raw events up to per-customer, per-period quantities | | **Price** | Apply markup, tiers, included credits, and overage | | **Collect** | Invoice, charge the card, retry failures, handle dunning | | **Reconcile** | Track *your* cost separately from *their* price to know margin | | **Comply** | Remit tax/VAT, absorb PCI scope, manage disputes | Whoever owns that last row is the merchant of record. With a DIY stack, that's you. With Macropay, that's us. ## Approach 1 — Build it yourself The do-it-yourself route is a real option, and for some teams the right one. Here's the shape of what you're signing up for. ``` ┌─────────────┐ ┌──────────────┐ ┌─────────────┐ │ Your App │───>│ LLM API │───>│ Response │ │ (prompt) │ │ (OpenAI, │ │ (tokens, │ │ │ │ Anthropic) │ │ usage) │ └─────────────┘ └──────────────┘ └──────┬──────┘ │ ┌─────────▼─────────┐ │ Usage Database │ │ (customer_id, │ │ tokens, model, │ │ cost, timestamp) │ └─────────┬──────────┘ │ ┌─────────▼──────────┐ │ Billing Engine │ │ (aggregate, price, │ │ invoice, collect) │ └──────────────────────┘ ``` To stand this up you'll write and then maintain: 1. A usage table keyed by customer, with tokens, model, and cost on every event 2. An aggregation layer that computes usage per customer per billing period 3. A pricing engine that applies your markup and produces a charge 4. A payment integration to actually collect — plus retries and dunning 5. A customer-facing view of usage and estimated charges Budget roughly **4–8 weeks** to a first version, then ongoing work every time a provider changes pricing or response shape. ### Three traps teams hit OpenAI returns `usage.prompt_tokens` and `usage.completion_tokens`. Anthropic returns `usage.input_tokens` and `usage.output_tokens`. Google returns `usageMetadata.promptTokenCount`. Support more than one model and you've built a normalization layer whether you wanted to or not. When a customer challenges a charge, "trust me, you used 4.2M tokens" doesn't hold up. You need per-request logs you can point to — not just monthly aggregates — which means storing and querying detail at scale. What you *paid* the model provider and what you *charge* the customer are separate numbers. Margin lives in the gap, and you can't see it without tracking both — a second accounting layer most teams discover late. ## Approach 2 — Run it on Macropay Macropay is usage-based billing infrastructure built for AI products. You instrument your LLM calls with the `@macropayments/ingestion` SDK; we handle metering, aggregation, invoicing, collection, tax, and the customer portal. ### Step 1 — Install the SDK ```bash npm theme={null} npm install @macropayments/ingestion ai @ai-sdk/openai ``` ```bash pnpm theme={null} pnpm add @macropayments/ingestion ai @ai-sdk/openai ``` ```bash pip theme={null} pip install macropay ``` ### Step 2 — Wrap your model with the LLM ingestion strategy The strategy intercepts each call and records token counts, model, and your cost automatically — attributed to the customer who made the request. **TypeScript (Vercel AI SDK)** ```typescript theme={null} import { Ingestion } from "@macropayments/ingestion"; import { LLMStrategy } from "@macropayments/ingestion/strategies/LLM"; import { generateText } from "ai"; import { openai } from "@ai-sdk/openai"; // One ingestion pipeline, reused across requests const search = Ingestion({ accessToken: process.env.MACROPAY_ACCESS_TOKEN, }) .strategy(new LLMStrategy(openai("gpt-4o"))) .cost((ctx) => ({ amount: ctx.metadata.totalTokens * 0.25, // your cost, in cents currency: "usd", })) .ingest("research-query"); export async function POST(req: Request) { const { question, customerId } = await req.json(); // A wrapped model that meters every call to this customer const model = search.client({ customerId }); const { text } = await generateText({ model, system: "You answer questions with cited sources.", prompt: question, }); return Response.json({ text }); } ``` **Python (PydanticAI)** ```python theme={null} import os from macropay.ingestion import Ingestion from macropay.ingestion.strategies import PydanticAIStrategy from pydantic_ai import Agent ingestion = Ingestion(os.getenv("MACROPAY_ACCESS_TOKEN")) strategy = ingestion.strategy(PydanticAIStrategy, "research-query") agent = Agent("gpt-4.1-nano") result = agent.run_sync("Summarize the latest filing for ACME Corp...") strategy.ingest("customer_123", result) ``` Every call now lands in Macropay with: * Input, output, and **cached** token counts * Model name and provider * Your cost for the request * The customer it belongs to Prefer to keep your code untouched? Point your OpenAI client at the Macropay AI proxy — an OpenAI-compatible endpoint at `POST /ai/v1/chat/completions`. It forwards the request and captures token cost on the way through, no SDK wiring required. ### Step 3 — Create a meter A meter turns raw events into a billable quantity. For token billing, sum `totalTokens` from your ingestion events. Go to the Meters section and choose **Create Meter**. * **Name**: LLM Tokens * **Filter**: events named `research-query` * **Aggregation**: sum of `metadata.totalTokens` Add a metered price to the product — for example, **\$0.01 per 1,000 tokens**. ### Step 4 — Customers watch it update live Usage and estimated charges appear in the self-serve customer portal and refresh as events arrive — so the invoice is never a surprise. ## Setting your margin Capturing usage is the easy half. Choosing what to charge is where products win or lose. Three patterns cover most AI businesses. ### Flat markup Add a fixed percentage on top of your model cost. Easy to reason about, easy to explain — but your margin moves whenever the provider's pricing does. | Your cost (per 1M tokens) | Markup | Customer price | Your margin | | -------------------------- | ------ | -------------- | ----------- | | \$2.50 (GPT-4o input) | 100% | \$5.00 | \$2.50 | | \$10.00 (GPT-4o output) | 100% | \$20.00 | \$10.00 | | \$0.15 (GPT-4o-mini input) | 200% | \$0.45 | \$0.30 | ### Volume tiers Lower the per-token rate as usage climbs, which rewards your biggest customers for consolidating spend with you. Configure the tiers directly on the metered price — no custom code. | Tier | Tokens / month | Price per 1K | | ---------- | -------------- | ------------ | | Starter | 0 – 100K | \$0.020 | | Growth | 100K – 1M | \$0.015 | | Scale | 1M – 10M | \$0.010 | | Enterprise | 10M+ | Custom | ### Prepaid credits Sell credits up front and burn them down per request. You get predictable revenue; the customer gets a price that's easy to budget against. Grant a credit allotment with each tier using the [meter credits benefit](/features/usage-based-billing/credits). ``` 1 credit = 1,000 tokens Starter: 10,000 credits / month ($29/mo) Pro: 100,000 credits / month ($199/mo) Enterprise: unlimited ($999/mo) ``` ## Beyond tokens: billing agents on outcomes Tokens are the obvious meter, but they measure *your* cost, not the *value* a customer receives. If you ship autonomous agents, Macropay lets you bill on what the agent did or achieved — not just what it consumed. * **Activity and outcome signals.** Send `POST /v1/signals` to record agent activity (a research run, a ticket resolved) or an outcome (a meeting booked, a refund prevented), attributed to the agent that produced it. * **Value receipts.** Certify ROI — time saved, revenue generated, cost avoided — so a customer sees the value behind the invoice, not just the line item. * **Agentic margin.** Compare billed revenue against the AI cost (COGS) behind each agent, so you know which agents actually earn their keep. This is the difference between charging for compute and charging for results — and outcome-based pricing is where agent products command the strongest margins. ## Worked example: an AI writing tool Here's the whole thing end to end for a tool that drafts blog posts, emails, and marketing copy. The model: a monthly subscription with included credits and metered overage. **Plans** | Plan | Price | Included | Overage | Seats | | ------ | -------- | ----------- | ------------ | --------- | | Writer | \$29/mo | 50K tokens | \$0.020 / 1K | 1 | | Team | \$99/mo | 250K tokens | \$0.015 / 1K | 5 | | Agency | \$299/mo | 1M tokens | \$0.010 / 1K | Unlimited | **Implementation** ```typescript theme={null} import { Ingestion } from "@macropayments/ingestion"; import { LLMStrategy } from "@macropayments/ingestion/strategies/LLM"; import { generateText } from "ai"; import { openai } from "@ai-sdk/openai"; const writer = Ingestion({ accessToken: process.env.MACROPAY_ACCESS_TOKEN, }) .strategy(new LLMStrategy(openai("gpt-4o"))) .cost((ctx) => ({ // Track real cost so margin stays visible amount: Math.ceil( ctx.metadata.inputTokens * 0.00025 + ctx.metadata.outputTokens * 0.001 ), currency: "usd", })) .ingest("writing-assistant"); export async function generateDraft( customerId: string, type: "blog" | "email" | "copy", brief: string, ) { const model = writer.client({ customerId }); const systemPrompts = { blog: "You are an expert blog writer...", email: "You are a professional email copywriter...", copy: "You are a conversion-focused marketing writer...", }; const { text } = await generateText({ model, system: systemPrompts[type], prompt: brief, }); return text; } ``` That's the entire billing integration. From there, Macropay: * Meters tokens per customer * Draws down the included credits on their tier * Bills overage at the right tier rate * Issues the invoice each period and collects the payment * **Remits sales tax and VAT** in every market you sell to * Surfaces live usage in the customer portal ## Know your margins in real time Pricing isn't set-and-forget — upstream model costs drift, and a power user who was profitable in January may not be in June. [Cost Insights](/features/cost-insights/introduction) puts your upstream LLM spend next to customer revenue so you can answer: * **Gross margin per customer** — are your heaviest users still in the black? * **Cost trend** — is a provider price change quietly eating your margin? * **Model efficiency** — which model gives the best margin for this workload? ## Start here Sign up and ship a metered product in under ten minutes. Events, meters, and metered pricing, explained. SDK reference for capturing token usage and cost. Track upstream costs and watch margins live. # Macropay vs Paddle: Which Merchant of Record Is Right for You? Source: https://docs.macropay.ai/blog/macropay-vs-paddle Macropay vs Paddle compared: pricing, tax handling as Merchant of Record, AI and agent billing, SDKs, and a step-by-step migration path. Both Macropay and Paddle act as your **Merchant of Record (MoR)** — meaning either one becomes the legal seller on your customer's statement, calculates and remits sales tax and VAT worldwide, and absorbs the PCI and chargeback burden so you don't have to. That part is table stakes. The real decision comes down to three things: **what you pay**, **how you bill** (especially if usage, tokens, or AI agents are involved), and **how fast you can ship**. This guide walks each one honestly. As a Merchant of Record, Macropay is the seller of record on every transaction. We register, collect, and file sales tax / VAT in the jurisdictions where your customers buy — and that liability sits with us, not you. The same is true of Paddle; it's the shared foundation both platforms are built on. ## TL;DR * **Cheaper base rate.** Macropay is 4.5% + 50¢; Paddle is 5% + 50¢. The gap compounds at volume. * **AI billing is native, not bolted on.** Token metering, an OpenAI-compatible proxy, cost insights, and **agent-level billing** ship out of the box. * **Digital delivery is built in.** License keys, file downloads, GitHub repo access, and Discord roles — no third-party tooling. * **Paddle wins on enterprise B2B.** Purchase orders, net-terms invoicing, and revenue recognition are areas where the incumbent is ahead. ## Where the two platforms overlap Paddle has spent over a decade building a mature MoR trusted by names like Notion and Framer. Macropay covers the same MoR fundamentals, then leans hard into usage-based and AI-native billing. Here's how the core capabilities line up. | Capability | Macropay | Paddle | | ---------------------------------- | -------------------------------------------- | --------------------------------- | | **Merchant of Record** | Yes | Yes | | **Tax registration & filing** | Global (EU VAT, UK VAT, US sales tax) | 200+ jurisdictions | | **Tax remittance** | Included | Included | | **Dispute / chargeback liability** | Handled as MoR | Handled as MoR | | **One-time products** | Yes | Yes | | **Subscriptions** | Upgrade, downgrade, pause, cancel | Upgrade, downgrade, pause, cancel | | **Usage-based billing** | Event meters, LLM ingestion strategies | Metered billing | | **AI / token billing** | Purpose-built (proxy, meters, agent signals) | General-purpose only | | **License keys** | Built in | Not built in | | **File downloads** | Built in | Not built in | | **GitHub repo access** | Automated benefit | Not available | | **Discord role grants** | Automated benefit | Not available | | **Customer portal** | Self-serve | Self-serve | | **Checkout** | Links, embed, sessions API | Overlay, inline, full page | | **Native SDKs** | TypeScript, Python, Go, PHP | JS, Python, PHP, Go, .NET | | **Framework adapters** | Next.js, Laravel, and more | Paddle.js (universal) | | **Webhooks** | Signed + retries | Signed + retries | | **Seat-based pricing** | Yes | Yes | | **Trials** | Yes (with abuse prevention) | Yes | | **B2B quotes / POs** | Not yet | Yes | | **Revenue recognition** | Not yet | Yes (accounting integrations) | ## Pricing, side by side Pricing is the clearest, most quantifiable difference. | | Macropay | Paddle | | ----------------------- | ------------------------------ | -------------------- | | **Base rate** | 4.5% + 50¢ | 5% + 50¢ | | **Monthly fee** | None | None (standard plan) | | **Setup fee** | None | None | | **International cards** | +1.5% (non-US) | Varies | | **Subscriptions** | +0.5% | Included | | **Disputes** | \$30 each | Varies | | **Payouts** | ACH / SEPA free, SWIFT at cost | Varies | ### What that looks like on real orders | Order | Macropay fee | Paddle fee | You keep extra | | ---------------------------- | ------------ | ---------- | -------------- | | \$40 one-time (domestic) | \$2.30 | \$2.50 | \$0.20 | | \$99 subscription (domestic) | \$5.45 | \$5.45 | \$0.00 | | \$120 one-time (domestic) | \$5.90 | \$6.50 | \$0.60 | | \$250 one-time (domestic) | \$11.75 | \$13.00 | \$1.25 | | \$80 subscription (intl) | \$5.45 | \$4.50 | −\$0.95 | For domestic and higher-ticket orders, Macropay is consistently cheaper, and the margin widens as order value grows. The one place Paddle can come out ahead is international subscriptions, where Macropay's +1.5% international and +0.5% subscription surcharges stack — so model your own mix before deciding. On **$100,000/month** in domestic processing, the half-point base-rate difference is roughly **$500/month** back in your pocket — about \$6,000 a year before factoring in dispute and payout fees. ## AI and agent billing: the real divergence If your product charges by tokens, API calls, compute, or **what an agent actually accomplishes**, this is where the platforms stop looking alike. Paddle has metered billing as a general capability. Macropay treats usage — and agents — as first-class. ### Meter usage with the AI proxy Point your OpenAI-compatible client at Macropay's proxy and token cost is captured automatically, per customer, with no manual tracking pipeline. ```bash theme={null} curl https://api.macropay.ai/ai/v1/chat/completions \ -H "Authorization: Bearer $MACROPAY_ACCESS_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "model": "gpt-4o", "messages": [{ "role": "user", "content": "Draft a launch tweet." }], "metadata": { "customer_id": "cus_8fK2p" } }' ``` Every call flows into a **meter**, the meter attaches to a metered price, and overage bills automatically — including against any prepaid **credits** you've granted with a plan. ### Bill agents by outcome, not just activity Agents are first-class in Macropay. The **Signals API** ingests both *activity* signals (the work an agent did) and *outcome* signals (what it achieved), attributed to the agent that earned them. ```bash theme={null} curl https://api.macropay.ai/v1/signals \ -H "Authorization: Bearer $MACROPAY_ACCESS_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "agent_id": "agt_research_42", "customer_id": "cus_8fK2p", "type": "outcome", "name": "lead_qualified", "value": 1 }' ``` From those signals you get **value receipts** that certify ROI to the customer (time saved, revenue generated, cost avoided) and an **agentic margin** view — billed revenue versus the underlying AI cost of goods for each agent. That lets you price on results with full visibility into whether each agent is actually profitable. ### Watch your COGS in real time **Cost insights** record cost events and cost traces for your upstream LLM spend, so per-customer margin and model efficiency are visible alongside revenue — not reconstructed in a spreadsheet after the fact. ### The same job on Paddle Paddle can support usage-based AI billing, but you supply the moving parts yourself: * Build token tracking and cost calculation * Run your own ingestion and aggregation pipeline * Stitch together reporting and per-customer margin * Ship a customer-facing usage dashboard Put simply: Paddle is a payments platform that *can* do AI billing with enough engineering. Macropay is a payments platform that *does* AI and agent billing on day one. ## Developer experience Both platforms are pleasant to build on. The differences are in emphasis. Framework adapters for Next.js, Laravel, and more · native SDKs for TypeScript, Python, Go, and PHP · full OpenAPI spec · an **MCP server** for agent tooling · a single customer-state call returning subscriptions, benefits, and usage together · OAuth2 + organization access tokens · a sandbox for payment-free testing. Paddle.js as a universal frontend library · SDKs for JavaScript, Python, PHP, Go, and .NET · a well-documented REST API · a full sandbox environment · long-standing, deep documentation. Net: Macropay leads on framework adapters, agent-ready tooling, and the consolidated customer-state API; Paddle leads on .NET support and the depth that comes with a more mature docs corpus. ## Choose Paddle when… * **You need the widest tax footprint.** Registration in 200+ jurisdictions matters if you sell heavily into markets like Japan, Brazil, or India. * **You sell enterprise B2B.** Purchase orders, custom invoicing, and net-30/60/90 terms are Paddle features Macropay doesn't offer yet. * **You need revenue recognition.** Paddle's accounting integrations cover ASC 606 / IFRS 15 workflows. * **You're a .NET shop.** Paddle ships a native .NET SDK. * **It already works.** If Paddle is serving you well, migration has a real cost — don't move for its own sake. ## Choose Macropay when… * **You're building AI or agents.** Native token metering, the AI proxy, the Signals API for agent activity and outcomes, value receipts, and agentic-margin reporting. * **Fees matter at scale.** 4.5% + 50¢ versus 5% + 50¢ is recurring savings, not a one-time discount. * **You deliver digital goods.** License keys, file downloads, GitHub access, and Discord roles are granted automatically as **benefits** — no extra tooling. * **You want to move fast.** Framework adapters, a clean API, a sandbox, and an MCP server for agent-native development. * **You'd rather own less compliance.** As your MoR we remit tax and VAT globally, stay PCI DSS Level 1 compliant, and handle disputes — so the tax and PCI liability stays off your plate. ## Migrating from Paddle to Macropay If you decide to switch, here's the path that minimizes disruption. [Sign up](https://macropay.ai/signup) and set up your organization. Rebuild each product with matching pricing, and attach any benefits — license keys, file downloads, repo or Discord access — you delivered before. Point webhook endpoints at your systems to keep them in sync. Macropay signs every payload and follows the Standard Webhooks spec, so signature verification is straightforward. Replace Paddle.js and Paddle API calls with the Macropay SDK. Embedded checkout drops in with a few lines: ```tsx theme={null} "use client"; import { MacropayCheckout } from "@macropayments/checkout"; export default function PricingPage() { return ( ); } ``` Let existing Paddle subscriptions run their current term. Route new sign-ups and renewals to Macropay so no active subscriber is disrupted. Confirm transactions, tax, and payouts are flowing correctly using Macropay's analytics before you scale traffic over. **About active subscriptions:** Macropay can't auto-migrate live subscriptions from Paddle. Let current subscriptions finish their term on Paddle and direct new ones to Macropay — a clean, low-risk transition. ## The bottom line Paddle is a proven, enterprise-ready MoR. If you're an established SaaS with B2B purchasing, net terms, and revenue-recognition requirements, it's a strong, safe choice. Macropay is the MoR built for what's coming next: AI products and autonomous agents. You get a lower base rate, billing that meters tokens and prices agents by outcome, automatic digital delivery, and full Merchant-of-Record coverage — global tax remittance, PCI DSS Level 1 compliance, and dispute handling — all in one flat fee. Spin up an account and accept your first payment in minutes — 4.5% + 50¢, all-in. Questions about moving off Paddle? We're glad to help map it out. # Why a Merchant of Record Beats Stripe for SaaS Companies Source: https://docs.macropay.ai/blog/why-mor-beats-stripe Stripe processes the payment but leaves you the tax bill. See why a Merchant of Record like Macropay ships global, compliant billing for one flat fee. ## The line item nobody quotes you A payment processor charges you to move money. A **Merchant of Record (MoR)** does that *and* becomes the legal seller of your product — which means the global tax, compliance, and chargeback burden lands on us, not on you. That single distinction is the whole story. Stripe is a great processor, and for plenty of teams it's the right call. But the moment you sell digital goods across borders, "processing payments" and "running a compliant billing operation" stop being the same job. This page is about the gap between the two — and who pays for it. ## What a Merchant of Record actually takes off your plate When Macropay is your MoR, your customer is buying from *us*. Macropay is the name on their card statement, the entity on the invoice, and the party a tax authority talks to. Concretely, we: * **Are the seller of record** — we carry the tax liability so you don't. * **Register, collect, and remit** sales tax, VAT, and GST in every jurisdiction your customers buy from. * **Issue compliant invoices** with the right tax treatment per country, automatically. * **Own the chargeback relationship** with the card networks, and work to keep dispute rates low. * **Absorb PCI scope** — cards are tokenized in our PCI DSS Level 1 compliant vault, so they never touch your servers. You ship product. We handle the part of selling it that has a legal department. ## Where Stripe quietly hands you the tax bill Stripe is a processor, not a seller of record. Sell globally on it and the obligations below become *yours*: | Jurisdiction | What you owe | | -------------------------- | --------------------------------------------------------------- | | EU | VAT across 27 member states, each with its own rate and rules | | UK | Separate post-Brexit VAT registration | | US | Sales tax in 45+ states, every one with its own nexus threshold | | Canada / Australia / India | GST / HST | | Everywhere else | A growing list of digital services taxes | Tooling can *calculate* the right amount to charge. Calculation is step one of four. You still have to **register**, **file**, and **remit** in every place you owe — and keep doing it as thresholds and rates change underneath you. A MoR collapses register → file → remit into a fee you already pay. There is nothing to integrate, no new jurisdiction to onboard, and no quarterly filing to miss. ## Run the real numbers The sticker fee is the cheap part. Here's the honest cost of "we'll handle tax ourselves" versus a MoR, on a hypothetical SaaS doing meaningful cross-border volume. ### DIY on a processor | Cost | Annual amount | | ---------------------------------------- | ------------------------------ | | Processing (\~2.9% + 30¢) | Variable | | Tax calculation add-on (\~0.5%/txn) | Variable | | Filing service, per jurisdiction | $1,200 – $3,000 each | | Accounting firm for compliance | $5,000 – $15,000 | | Engineering to build the tax integration | 2–4 weeks up front | | Ongoing tax-rule maintenance | 1–2 days/month | | **Effective rate on \$100** | **\~5–7% + heavy fixed costs** | ### Macropay (Merchant of Record) | Cost | Annual amount | | ------------------------------------ | ---------------------- | | Processing (4.5% + 50¢) | Variable | | Tax calculation, filing & remittance | Included | | Registration management | Included | | Engineering | 1–2 hours | | Ongoing maintenance | None | | **Effective rate on \$100** | **4.5% + 50¢, all-in** | The headline gap between 2.9% + 30¢ and 4.5% + 50¢ evaporates once the fixed compliance stack is on the table. Past a handful of jurisdictions, the MoR is frequently the *cheaper* line — and it's always the simpler one. Selling internationally adds **+1.5%** on non-US cards and subscriptions add **+0.5%**, both already inside the all-in rate — no separate tax SKU, no surprise invoice. ## Chargebacks: who's actually on the hook On a processor, *you* are the merchant. When a customer disputes a charge: * You eat the dispute fee whether you win or lose. * You assemble evidence and respond inside tight deadlines. * Cross a dispute-rate threshold (\~0.7%) and you land in a card-network monitoring program with penalties. * Sustained problems can get your account frozen or closed. With Macropay as MoR, the dispute is **ours** to manage. We respond on your behalf, work proactively to keep rates under network thresholds — including preemptive refunds when that's the cheaper outcome — and stand between your business and direct network penalties. Our dispute fee is a flat **\$30**, win or lose, with nothing else hidden behind it. ## The AI-billing edge Most "Stripe vs. MoR" comparisons stop at tax. If you're building AI products, there's a second axis that matters just as much: **how you bill for consumption, and how an agent gets paid for the work it does.** Macropay is built for usage- and outcome-based pricing, not bolted onto it: * **Meter by usage, activity, or outcome.** Ingest **events**, roll them into **meters**, attach a metered price, and settle against prepaid **credits**. * **OpenAI-compatible AI proxy** at `/ai/v1` — swap your base URL and token cost is captured per request, automatically. * **Agents are first-class.** Stream activity and outcome **signals** to attribute work to a specific agent, mint **value receipts** that certify ROI (time saved, revenue generated, cost avoided), and watch **agentic margin** — billed revenue against AI cost (COGS) — per agent. Bill an AI feature in a few lines: ```bash theme={null} # Send the AI proxy request — token cost is captured automatically curl https://api.macropay.ai/ai/v1/chat/completions \ -H "Authorization: Bearer $MACROPAY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "gpt-4o-mini", "messages": [{"role": "user", "content": "Draft a release note for v2.3"}] }' ``` ```bash theme={null} # Attribute an outcome to an agent — rolls up into a value receipt curl https://api.macropay.ai/v1/signals \ -H "Authorization: Bearer $MACROPAY_ACCESS_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "signals": [{ "kind": "outcome", "event_name": "ticket_resolved", "agent_id": "agent_support_triage", "external_customer_id": "cus_8421", "external_id": "ticket_8Qp2", "data": { "tickets": 1 } }] }' ``` No processor gives you this. It's the difference between billing for an API call and billing for the result an agent delivered. ## So, who should pick which A MoR isn't the answer for every business. Be honest about your case. You need total control over the payment flow, sell B2B inside one simple-tax country, already run a multi-jurisdiction finance team, need custom money movement (marketplace splits, complex payouts), or process volume so high that per-transaction rate beats compliance cost. You're a small-to-mid SaaS selling across borders, have no tax team and don't want one, want to launch globally on day one, are building AI products that need usage or outcome metering, or simply want to ship product instead of tax infrastructure. ## Bottom line A processor hands you a payment API. A Merchant of Record hands you a billing *business* — tax, compliance, disputes, and AI metering already wired in. The base-rate difference is real, and it stops mattering the moment you price in what it actually costs to sell globally and stay compliant. Open an account and accept payments in minutes — zero tax registration on your side. All-in pricing, nothing hidden: 4.5% + 50¢ per transaction. # API Changelog Source: https://docs.macropay.ai/changelog/api Stay up to date with the latest changes, improvements and deprecations to the Macropay API. ## Checkout API and Customer Session API changes To be more consistent across our API, we've renamed `customer_external_id` field to `external_customer_id` in the Checkout API and Customer Session API. * **Deprecated**: `customer_external_id` field in the Checkout API and Customer Session API. Use `external_customer_id` instead. ## Benefit metadata in Customer State The customer state now includes the [benefit metadata](/api-reference/customers/state#response-benefit-metadata) in the `granted_benefits` list. ## Webhook API endpoints are now documented The API endpoints for managing webhooks are now documented in the API reference, and fully supported in our SDK. [Read more](/api-reference/webhooks/endpoints/create) ## Rate limits To ensure fair usage and maintain performance, we've introduced rate limits for the API. The limits are as follows: * **100 requests per second** per IP address. ## Order invoice generation and retrieval Until now, the invoice was generated automatically when the order was created, allowing you to call [`GET /v1/orders/{id}/invoice`](/api-reference/orders/get-invoice) and [`GET /v1/customer-portal/orders/{id}/invoice`](/api-reference/customer-portal/orders/get-invoice) endpoints without any prior action. We now require you to explicitly generate the invoice by calling the [`POST /v1/orders/{id}/invoice`](/api-reference/orders/post-invoice) or [`POST /v1/customer-portal/orders/{id}/invoice`](/api-reference/customer-portal/orders/post-invoice) endpoints. This change allows us to better handle the invoice generation process, and to allow the customer to change the billing details (name and address) before generating the invoice. This can be done through the [`PATCH /v1/orders/{id}`](/api-reference/orders/patch) or [`PATCH /v1/customer-portal/orders/{id}`](/api-reference/customer-portal/orders/patch) endpoints. ## Benefit metadata support and floating point numbers in metadata * **Added**: Benefits now support [metadata](/api-reference/benefits/create#body-metadata). * **Added**: Metadata values now support floating-point numbers. Before, only strings, integers and booleans were supported. ## Checkout amount fields changes and depreciations To be more consistent with the [Order schema changes](#2025-03-14), we've made some changes to the field related to amounts in the Checkout schema. * **Added**: [`checkout.discount_amount`](/api-reference/checkouts/get-session#response-discount-amount). * **Added**: [`checkout.net_amount`](/api-reference/checkouts/get-session#response-net-amount). * **Deprecated**: `checkout.subtotal_amount`, use [`checkout.net_amount`](/api-reference/checkouts/get-session#response-net-amount) instead. ## New order status and webhooks Until now, Macropay only kept track of fully processed, **paid** orders. To help you keep track of the order lifecycle, we've added a new status `pending`, which is a transitive state meaning the order is created but not paid yet. In most cases, the order will transition from `pending` to `paid` in a short time. * When receiving `order.created` event, the order status might not be `paid`. * **Added**: [`order.updated`](/api-reference/webhooks/order.updated) webhook, sent when the order status changes or when it's partially or fully refunded. * **Added**: [`order.paid`](/api-reference/webhooks/order.paid) webhook, sent when the order is fully processed and paid. * **Added**: [`Order.paid`](/api-reference/orders/get#response-paid) property to the order schema. If you were relying on the `order.created` webhook to keep track of succesful orders, we recommend you to switch to `order.paid`. ## Subscriptions and Orders schema changes To prepare our next move to support usage-based billing, we've made some changes to the [`Subscription`](/api-reference/subscriptions/get) and [`Order`](/api-reference/orders/get) schemas. The main reason behind those is that we need to support multiple prices and items in a single subscription or order. * **Deprecated**: `Subscription.price_id` and `Subscription.price`. Use the `Subscription.prices` array instead. * **Deprecated**: `Order.product_price_id` and `Order.product_price`. Use the `Order.items` array instead. * **Deprecated**: `Order.amount`. Use the `Order.net_amount` instead. It has the same value and meaning, but the new name is more explicit. * **Added**: `Order.subtotal_amount`, `Order.discount_amount`, and `Order.total_amount` fields to provide a more detailed breakdown of the order amount. # Product Updates Source: https://docs.macropay.ai/changelog/recent Stay up to date with the latest changes and improvements to Macropay. ## This week A big week. Agent Billing, a native Affiliate program, CRM sync, growth tooling, and a full dispute workflow all shipped — plus a long list of billing and checkout reliability fixes. ### New * **Agent Billing is live.** Track agents as first-class entities, bill on activity or outcomes, and watch margin per agent and per model in the new Agents dashboard. Value Receipts give buyers certified ROI for every run. Python and TypeScript SDKs both ship with `agent.setup_billing`, automatic tool-cost capture (`track_tool` / `track_cost`), and verified-vs-reported outcome splits. Start with [Agents](/features/agents/introduction), [Signals](/features/agents/signals), [Margin](/features/agents/margin), [Value Receipts](/features/agents/value-receipts), and the [SDK](/features/agents/sdk). * **Native Affiliate program.** Run your own affiliate program from the dashboard — invite by email, CSV, or share link, ship branded creatives and tracked links, and let referrals auto-attribute through hosted checkout with zero merchant JS. The public marketplace at [affiliate.macropay.ai](https://affiliate.macropay.ai) is a searchable directory of live programs. See [Affiliates](/features/affiliates/introduction) and [Setup](/features/affiliates/setup). * **Affiliate payouts.** Manual payout tooling with configurable hold windows, per-affiliate rollups, and one-click PayPal Payouts (opt-in). Pay-with-fee records payouts net of the actual processing fee — no Stripe Connect required. Details in [Payouts](/features/affiliates/payouts). * **CRM sync for Salesforce and HubSpot.** Connect from Settings → CRM to keep deals in sync with MRR, ARR, total paid, and contract terms. Lifecycle events (new customer, churn, expansion) flow through as they happen. One-click field mapping in the sync settings page. * **Growth: free → paid upgrades.** A complete PLG flow ships this week — in-product upgrade with card collection, smart-upgrade triggers based on usage, hard-limit entitlement gates, and a new Growth dashboard for rule CRUD. Two new webhook events, `usage.threshold_reached` and `subscription.upgrade_recommended`, let you wire upgrade prompts into your own UI. * **Dispute management.** Chargebacks now pull automatically from Stripe with the evidence deadline attached. AI drafts a first-pass evidence response (with safe fallback), routes it through an approval workflow, and posts Slack notifications across the lifecycle. The backoffice shows evidence due-by at a glance. * **MRR Movements + SaaS health metrics.** A stacked-bar breakdown of new, expansion, contraction, and churned MRR, plus derived metrics (NRR, GRR, quick ratio) and an Annual Run Rate surface on the analytics home. * **Cohort retention heatmap.** Drill into any cohort to see its customer list, filter by country, and export the full matrix as CSV. * **System Errors dashboard.** Payment failures are now classified as customer-caused vs system-caused, persisted, and surfaced in a new dashboard widget and page. Merchants get an alert on the first system failure. * **Bulk customer import + paid-outside billing.** Import customers in bulk, attach subscriptions that are billed outside Macropay (excluded from revenue), and mark individual orders as paid outside. Useful for migrating an existing book. * **Merchant-created subscriptions.** Add a subscription directly from the customer page — charge an existing card on file or track a paid-outside subscription. * **MCP agentic commerce tools.** New opt-in tools on [`mcp.macropay.ai`](/features/integrations/mcp) let agents create checkouts and record usage. Off by default — flip the agentic commerce toggle to enable. See [MCP agentic commerce](/features/integrations/mcp-agentic-commerce). * **x402 agentic payment rails (preview).** Initial HTTP 402 support for agent-to-merchant payments, opt-in and off by default. Read more in [x402](/features/integrations/x402). * **Dark mode toggle.** Light/dark toggle now lives in the dashboard topbar and on the login and signup pages. ### Updates * **Webhooks DX.** A test-event button on every endpoint, clearer paid-event copy, and 404 → endpoint redirects. The verification flow is now fully [Standard Webhooks](/integrate/webhooks/verification) compliant, and sandbox email checks have been relaxed for faster local iteration. * **Tax copy.** The pricing and tax pages now make it explicit that Macropay — not the merchant — files and remits sales tax, VAT, and GST in every supported jurisdiction. * **Products page performance.** Eager-loaded schema relationships cut about 10 seconds off list loads on large catalogs. * **Backoffice approve.** Merchant review can now approve an org from any status, including `created` → `active` in one click. ### Fixes * **AI proxy billing, end-to-end.** Round-once billing, correct reservation release on cancel, proper streaming usage capture, and FX-normalized AI cost so margin reports match invoices. * **Renewal double-charge.** Fixed a race that could charge a renewal twice when a budget overflow retried. * **Checkout reliability.** 3DS finalize now has a backup path, stale payment locks are cleaned up, and a reusable payment-method token is persisted on successful charges so re-subscribe keeps the saved card. * **Bank details detection.** Saving payout bank details now correctly flips the account to "details submitted," unblocking promotion to active. * **MCP server.** OAuth tokens now resolve correctly and `/ai/v1` is rate-limited per key, fixing intermittent 401s and noisy-neighbor throttling. * **Security.** Tightened KYC org checks, hardened Didit webhook signature verification, and namespaced idempotency keys per organization. * **Affiliate referral attribution.** Referral links now route through a server-side `/track` endpoint, so attribution works even when buyers block third-party JS. * **Signup access.** New customers can now reach the signup page again, and the dark-mode trust panel no longer flashes white before paint. * **Cmd+K.** Quick actions refresh on open and show the right set for the current page. ## This week A round of checkout, onboarding, and AI proxy improvements following the 1.0 launch. ### New * **3DS for EU / UK / IN cards.** Checkout now bridges Basis Theory tokenization to Stripe's 3DS challenge flow, so European, British, and Indian buyers can complete Strong Customer Authentication without leaving the hosted checkout page. * **Basis Theory is the default for every new organization.** All new merchants get Basis-Theory-tokenized card capture out of the box, which keeps PCI scope at SAQ-A. Read more in [Basis Theory checkout](/features/checkout/basis-theory). ### Updates * **Faster path to live.** Organizations now auto-promote to **active** as soon as bank-wire MoR prerequisites are satisfied — no more waiting on a separate identity step. The Stripe Identity surface has been removed from onboarding entirely; KYC runs through our in-house document upload. * **LLM inference accepts publishable tokens.** Calls to `ai.macropay.ai` now authenticate with `pk_` tokens, so you can proxy OpenAI, Anthropic, Google, and OpenRouter from client-side code paths that don't have a secret key. See [LLM inference](/features/llm-inference). * **Approve button restored in backoffice.** Orgs in `onboarding_started` are now actionable from the merchant review queue. ### Fixes * **No more duplicate charges on retry.** The checkout idempotency key now includes the Basis Theory / Stripe `confirmation_token`, so retried submissions on the same order can't double-charge a card. * **Payments gated on real bank details.** Checkout now blocks payment collection until the merchant's payout bank account is on file, and surfaces a clear message to the buyer if it isn't. This prevents successful charges that can't be paid out. * **Checkout submit reliability.** The Basis Theory card element is now consistently discovered at submit time, fixing an intermittent "card not tokenized" error on the first click. * **MCP server stability.** Production routing, OAuth client persistence, and tool registration fixes for [`mcp.macropay.ai`](/features/integrations/mcp) — all 53 tools are back online for Claude Desktop, Cursor, ChatGPT, and Claude Code. ## Macropay 1.0 — We're live Macropay is now generally available as a Merchant of Record for SaaS, digital goods, and AI-native businesses. One platform handles checkout, billing, global tax, KYC, fraud, chargebacks, and payouts — so you can ship and get paid in 175+ countries without touching compliance. Here's everything that ships with 1.0. ### Merchant of Record, end-to-end Sales tax, VAT, GST collected and remitted on your behalf in 60+ jurisdictions. Macropay is the seller of record on every invoice. We absorb chargebacks and dispute them with the network. You don't log into Stripe to upload evidence — we handle it. Sub-second device fingerprinting, IP risk scoring, AI-assisted seller KYC review. Buyers don't see the friction; you don't see the fraud. Direct bank wires to your business account in 40+ currencies. No Stripe Connect, no Express dashboard — you get paid like a vendor, not a sub-account. ### Checkout you'd actually link to * **Hosted checkout** powered by Basis Theory tokenization — PCI scope drops to SAQ-A, you never touch a PAN. * **Adaptive payment methods**: cards, Apple Pay, Google Pay, SEPA, iDEAL, Bancontact, BLIK, and 25+ local methods that surface automatically by buyer country. * **Currency localization**: prices auto-convert with up-to-the-hour FX, displayed in the buyer's currency, settled in yours. * **One-click upsells** post-purchase without re-collecting card details. * **Light-mode-locked checkout page** for predictable rendering inside any merchant theme. ### Subscriptions, the way they should work Meter events via API or the Events stream, define pricing tiers in the dashboard, Macropay handles proration and invoicing. Self-serve cancellation, payment-method updates, invoice history. White-labeled to your domain. Free trials, percentage and fixed-amount discount codes, seat-based pricing, scheduled price changes. Smart retry windows tuned per issuer, branded email sequences, automatic pause/resume. ### MCP server: your business as an AI tool catalog Macropay ships an **official remote MCP server** at [`mcp.macropay.ai`](https://mcp.macropay.ai). Add the URL to Claude Desktop, Cursor, ChatGPT, or Claude Code, click through the OAuth prompt once, and you have **53 tools** wired up: 45 auto-generated from the OpenAPI spec (customers, products, subscriptions, orders, payments, refunds, members, metrics, meters), plus 7 hand-authored extras. Ask in plain English: * *"What's my MRR right now and how does it compare to last week?"* * *"Top 10 customers by 30-day revenue."* * *"Refund order ord\_abc fully."* * *"Why did payment ch\_xyz fail?"* * *"Create a \$49/mo plan called AI Pro."* Read the full guide: [MCP server](/features/integrations/mcp). ### Slack integration One-click OAuth installs Macropay into your workspace and pipes **five event types** to any channel: * New paid order * Refund issued * Chargeback opened * Failed payment (after retries) * New customer signup Plus a **daily revenue summary** at the hour you pick, and slash commands so you can pull numbers without leaving Slack: ``` /macropay revenue today /macropay mrr /macropay churn 30d /macropay help ``` ### LLM inference billing (`ai.macropay.ai`) If your product calls an LLM, Macropay can be your billing layer. We proxy OpenAI, Anthropic, Google, and OpenRouter through a single endpoint, meter every token, and add a margin you control — then bill it through the same subscription or usage line the rest of your product uses. No separate Stripe meter to wire up. Read the full guide: [LLM inference](/features/llm-inference). ### Migrate in under two weeks Moving from Lemon Squeezy, Paddle, Stripe Billing, Gumroad, or Chargebee? Our team runs the migration end-to-end: 30 minutes. We map your catalog, customers, and active subscriptions. Full replication in a Macropay sandbox. You test before any customer-facing change. \< 1 hour for accounts up to 10,000 active subscriptions. Customers don't re-authorize payment. Dedicated Slack channel for the cutover and the two weeks after. Migration is **free**. [Email sales@macropay.ai](mailto:sales@macropay.ai?subject=Migrate%20to%20Macropay) and we'll reply within one business day. ### Developer surface * **REST API** with idempotency keys, cursor pagination, and an OpenAPI 3.1 spec. * **Webhooks** signed per the [standard-webhooks](https://www.standardwebhooks.com/) spec — drop-in compatible with most existing webhook frameworks. * **TypeScript SDK** + Python SDK, auto-generated from the OpenAPI spec. * **Sandbox mode** with a shared test card, simulated webhooks, and test clocks so you can fast-forward subscription cycles. * **Organization Access Tokens** (OATs): org-scoped, scope-limited bearer tokens for server-to-server use. Mode-aware (live vs sandbox) and revocable from the dashboard. ### Onboarding in 4 steps Sign in with Google or email, pick a slug, you're in the dashboard in 30 seconds. Legal entity, payout bank account, tax IDs. Stored encrypted. AI-assisted — we cross-check your business against public signals (Trustpilot, BBB, Reddit, web presence). Most merchants approved within a business day. Flip from sandbox to live, paste your checkout URL on your site, take orders. ### What's next We're already working on: * **Vector-DB-backed docs search** inside the MCP server * **Stripe-compatible webhook payloads** for drop-in migration * **More local payment methods** in LatAm and SEA * **Affiliate / partner payouts** as a first-class object Have a feature you want? [sales@macropay.ai](mailto:sales@macropay.ai) or ping us in the merchant Slack — we read every message. Sign up free. Sandbox immediately, live the same week. # For affiliates Source: https://docs.macropay.ai/features/affiliates/for-affiliates Promote a merchant, earn commission. Apply, grab your referral link, and track clicks, conversions, and payouts in one portal. You're an affiliate: you promote a merchant's product, and when someone you referred buys, you earn a commission. This page walks you through applying, getting your referral link, and tracking what you've earned — all from the merchant's affiliate portal. You don't need a Macropay account. Everything happens through the merchant's portal using just your email — no password to manage. ## How it works in four steps The merchant shares an application link that looks like this: ``` https://affiliate.macropay.ai/affiliate/apply?org= ``` The `org` value identifies the merchant whose program you're joining. Fill in your details and submit. The merchant reviews applications and approves the affiliates they want to work with. Once you're approved, open the portal: ``` https://affiliate.macropay.ai/affiliate?org= ``` Enter the same email you applied with. We send a one-time code to that inbox — paste it in and you're signed in. No password, nothing to reset. Inside the portal you'll find your personal referral link. Share it anywhere your audience is — a blog post, a YouTube description, a newsletter, a social post. Every click through that link is tagged to you. The portal shows your performance in near real time: how many people clicked, how many converted into paying customers, and how much commission you've accrued. The merchant settles your payments and marks each commission as paid once it's sent. ## How referrals are attributed Macropay uses **last-click attribution**. When a customer follows your referral link, a cookie ties that visit to you. If that customer goes on to buy, you get the credit — provided yours was the most recent affiliate link they clicked before purchasing. If a customer clicks your link, then later clicks a different affiliate's link before buying, the most recent click wins. Last click takes the commission. ## What you'll earn Your commission rate, the products it applies to, the attribution window, and any minimum payout are all set by the **merchant's program terms** — not by Macropay. Two merchants can run very different programs, so always check the terms shown in the portal when you apply. Pick referral links that point to the exact product or offer you're promoting. The cleaner the path from your content to checkout, the better your conversion rate. ## FAQ **How do I get paid?** The merchant pays you directly. Macropay tracks every click, conversion, and the commission you've earned, and gives the merchant a clear view of what's owed. When they send your payment, they mark the commission as **paid** in the portal so your balance stays accurate. **When does a referral count?** A referral counts when someone clicks your link and then completes a qualifying purchase, with yours being the last affiliate link they clicked. Whether a specific purchase qualifies — and how long the attribution window stays open — depends on the merchant's program terms. **How do I see my earnings?** Sign in to the portal at `https://affiliate.macropay.ai/affiliate?org=` with your email and the one-time code. Your dashboard breaks down clicks, conversions, and commission earned, along with what's been marked paid versus still pending. **Do I need an account or a password?** No. You sign in with your email and a one-time code sent to your inbox each time. There's no password to create or remember. **Can I promote more than one merchant?** Yes. Each merchant runs its own program under its own `org` link, so you apply separately for each one and switch between portals using the matching application and sign-in links. ## Related See how merchants package the products you'll be promoting. Understand who sells, who handles tax, and how payouts are settled. # Affiliates Source: https://docs.macropay.ai/features/affiliates/introduction Run a native affiliate program inside Macropay — recruit partners, track referrals, and pay commission on the sales they drive. Affiliates is a new feature. We're shipping fast and actively want your feedback. ## Grow revenue with partners who get paid for results Macropay Affiliates lets you run a native partner program — recruit affiliates, give each a unique referral link, and accrue commission on every sale they drive — without bolting on a third-party tool. Referrals, attribution, and earnings all live in the same dashboard where you already manage products, customers, and payments. It's built for the way modern software sells: partners promote your product, Macropay attributes the resulting purchases, and you see exactly what each affiliate earned — so you can reward the people sending you real revenue. ## How it works A referral travels through five stages, all tracked for you: 1. **Affiliates apply** — partners sign up to your program through a hosted application page. 2. **You approve** — review and accept each applicant, or turn on auto-approve to onboard instantly. 3. **They get a referral code/link** — every approved affiliate receives a unique code and shareable link. 4. **Macropay attributes the sale** — when a referred buyer purchases, we match the sale to the affiliate and accrue a commission. 5. **You review and mark paid** — track accrued earnings, export them, and mark them settled. **v1: you settle with affiliates; Macropay tracks and reports.** Macropay records every referred sale, calculates commission, exports a payout CSV, and lets you mark earnings as paid. Sending the money to your affiliates is done by you in this version — we handle the tracking, math, and bookkeeping. ### Commission models You decide how partners earn. Macropay supports: | Model | How it pays | Good for | | --------------------------- | ----------------------------------------------------------------------- | -------------------------------------------------------------- | | **Percentage of sale** | A % of each referred order's value | Most programs; scales with order size | | **Fixed per conversion** | A flat amount per referred purchase | Predictable payouts, low-ticket products | | **Recurring on renewals** | Commission on each subscription renewal, with an optional **month cap** | Subscriptions where partners earn over the customer's lifetime | | **Per-affiliate overrides** | A custom rate for a specific partner | Negotiated deals, high-value influencers | Use a **month cap** on recurring commission to reward partners for the customers they bring while keeping your lifetime payout per referral bounded — e.g. pay on the first 12 renewals, then stop. ### Attribution Macropay uses **last-click attribution within a configurable cookie window**. When a visitor follows an affiliate's link, we set a cookie; if they purchase before it expires, the most recent affiliate to refer them earns the commission. You set the window length to match your typical sales cycle. ## Merchant of Record context Because Macropay is your **[Merchant of Record](/merchant-of-record/introduction)**, we are the legal seller on every referred order — we compute and remit global sales tax and VAT on the underlying sale. Affiliate commission is calculated on the order value you choose to base it on, and the tax machinery on the sale itself stays our responsibility, not yours. ## Launch your program Set a default commission model and rate, choose your cookie window, and decide whether applications are auto-approved or reviewed manually. See [Set up](/features/affiliates/setup). Share your application page with potential partners. Approve applicants (or let auto-approve do it), and set per-affiliate overrides for negotiated deals. Each approved partner gets a unique referral code and link to drop into content, emails, and social posts. See [For affiliates](/features/affiliates/for-affiliates). When a referred buyer purchases within the cookie window, Macropay matches the sale to the affiliate and accrues commission against it. Track accrued commission per affiliate, export a payout CSV, settle with your partners, and mark the earnings paid. See [Earnings & payouts](/features/affiliates/payouts). ## Explore Configure commission models, cookie window, approvals, and per-affiliate overrides. Review accrued commission, export payout CSVs, and mark earnings as paid. How partners apply, get their referral link, and track what they've earned. ## FAQ Not in v1. Macropay **tracks** every referred sale, **calculates** commission, lets you **export a payout CSV**, and lets you **mark earnings as paid** — but you send the actual payment to your affiliates yourself. We own the attribution, the math, and the reporting; you handle the transfer. Four models, mixable per program and per partner: **percentage of sale**, **fixed per conversion**, **recurring on subscription renewals** (with an optional month cap), and **per-affiliate overrides** for custom negotiated rates. Macropay uses **last-click attribution within a configurable cookie window**. Following an affiliate's link sets a cookie; if the visitor purchases before it expires, the most recent referring affiliate earns the commission. You choose the window length. No. Affiliates is native to Macropay — recruitment, referral links, attribution, and earnings all live alongside your products, customers, and payments, with no external integration to maintain. # Earnings & payouts Source: https://docs.macropay.ai/features/affiliates/payouts Track accrued affiliate commission, export it for accounting, and mark conversions paid once you settle with your affiliates. Macropay tracks every affiliate conversion and the commission it accrues, so you always know what you owe and what you have already paid. In v1, Macropay does **not** move money to your affiliates — you pay them with your own method (bank transfer, PayPal, gift card, however you settle) and mark the conversions paid for your records. Macropay does not send payouts to affiliates. The Earnings view is a ledger for tracking accrued commission and recording your own settlements. You remain responsible for paying affiliates and for any tax reporting that applies. ## Conversion lifecycle Every referred sale becomes a conversion. Its status tells you where the commission sits: | Status | Meaning | | ---------- | ------------------------------------------------------------------------------------ | | `pending` | Commission accrued from a sale, not yet cleared (e.g. still inside a refund window). | | `approved` | Commission cleared and owed to the affiliate — ready to pay. | | `paid` | You settled with the affiliate and marked the conversion paid. | | `reversed` | The underlying sale was refunded or disputed; the commission is clawed back. | A conversion typically moves `pending → approved → paid`. A refund or dispute moves it to `reversed`, removing it from what you owe. ## Earnings in the dashboard Open **Affiliates → Earnings** to review and settle commission. The header shows total conversions, **owed** (unpaid `approved` commission), and **paid** to date — so you can see your outstanding affiliate liability at a glance. Filter by affiliate and by conversion status to isolate exactly what you intend to settle this cycle. Click **Export CSV** to download the filtered conversions for bookkeeping, reconciliation, or your payout spreadsheet. Settle with your affiliates using your own payment method, then select the conversions and use bulk **Mark paid** to record the settlement. Use the **batch ID** when marking paid to group everything from one payout run (for example, a monthly bank transfer batch). It makes reconciliation against your bank statement straightforward. ## Payouts tab — settle per affiliate, net of fees **Affiliates → Payouts** rolls commission up **per affiliate** so you can pay each one in a single transfer to the PayPal or bank details they saved in their portal. | Column | Meaning | | ------------- | ------------------------------------------------------------------------------------------------------------------------------------ | | **Held** | Commission still inside your **payout hold window** — a refund/chargeback buffer. Set the days in **Program** settings (default 30). | | **Available** | Commission past the hold window, ready to pay. | | **Paid** | The **net** you've already sent (after fees). | To settle an affiliate, click **Pay**, enter the **actual fee** your bank or PayPal charged, and Macropay shows the **net to send** (gross − fee) and records the payout. **Export CSV** gives every affiliate's amount + payout method/details for a bulk run. Each settlement is logged under **Recent payouts** with gross, fee, and net. Macropay records the payout — **you send the net** with your own method; it never moves money to affiliates. ### One-click PayPal payouts (optional) If you connect a PayPal Business account (with Payouts enabled), affiliates who chose **PayPal** as their method get a **Send via PayPal** button on the Pay dialog — it sends the net straight to their PayPal email via the PayPal Payouts API and records the payout, no manual transfer. Bank/Wise affiliates continue to use the export + your own transfer. PayPal is off until credentials are configured; set it up in **sandbox** first, verify a test payout, then switch to live. ## API reference All endpoints use the base URL `https://api.macropay.ai` and a bearer token. ### List conversions `GET /v1/affiliates/conversions` returns conversions, with optional filters for `affiliate_id` and `conversion_status`. ```bash cURL theme={null} curl https://api.macropay.ai/v1/affiliates/conversions \ -G \ --data-urlencode "affiliate_id=aff_8842" \ --data-urlencode "conversion_status=approved" \ -H "Authorization: Bearer " ``` ```python Python theme={null} import requests resp = requests.get( "https://api.macropay.ai/v1/affiliates/conversions", params={"affiliate_id": "aff_8842", "conversion_status": "approved"}, headers={"Authorization": "Bearer "}, ) conversions = resp.json() ``` ```typescript TypeScript theme={null} const params = new URLSearchParams({ affiliate_id: "aff_8842", conversion_status: "approved", }); const resp = await fetch( `https://api.macropay.ai/v1/affiliates/conversions?${params}`, { headers: { Authorization: "Bearer " } }, ); const conversions = await resp.json(); ``` ### Export conversions as CSV `GET /v1/affiliates/conversions/export` returns the same filtered set as a CSV file — ideal for handing to accounting. ```bash cURL theme={null} curl https://api.macropay.ai/v1/affiliates/conversions/export \ -G \ --data-urlencode "conversion_status=approved" \ -H "Authorization: Bearer " \ -o affiliate-conversions.csv ``` ### Mark conversions paid `POST /v1/affiliates/conversions/mark-paid` records that you have settled the listed conversions. Pass the `conversion_ids` to settle and an optional `batch_id` to tag the payout run. ```bash cURL theme={null} curl https://api.macropay.ai/v1/affiliates/conversions/mark-paid \ -X POST \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -d '{ "conversion_ids": ["conv_a1b2", "conv_c3d4", "conv_e5f6"], "batch_id": "payout_2026_05" }' ``` ```python Python theme={null} import requests resp = requests.post( "https://api.macropay.ai/v1/affiliates/conversions/mark-paid", headers={"Authorization": "Bearer "}, json={ "conversion_ids": ["conv_a1b2", "conv_c3d4", "conv_e5f6"], "batch_id": "payout_2026_05", }, ) result = resp.json() ``` Marking a conversion paid only updates your Macropay records — it does not trigger any transfer. Always complete the actual payment to your affiliate first, then mark the conversions paid. ## Where to send the money Each affiliate provides their own payout details from their portal, so you always know where to send commission. In their portal (`/affiliate?org=`) the affiliate opens **Payout details**, picks a method — **PayPal**, **bank transfer**, **Wise**, or **Other** — and fills in the fields (e.g. PayPal email, or account holder * IBAN/account number + SWIFT/routing + country). On **Dashboard → Affiliates**, expand an affiliate ("View details, payout & earnings") to see their earnings rollup (earned / owed / paid), the payout details they entered, and their conversions — with a per-affiliate **Mark N paid** button. Send the payment with the method they specified, then mark their pending conversions paid. Payout details are provided and owned by the affiliate — you view them to pay, but you don't edit them. If an affiliate hasn't added details yet, their detail view shows "Not provided yet". ## FAQ **Does Macropay pay my affiliates?** No. In v1, Macropay tracks accrued commission and lets you record settlements, but it does not move money to affiliates. You pay them with your own method and use **Mark paid** (or the `mark-paid` endpoint) to keep your ledger accurate. **How do I export earnings for accounting?** Filter the conversions table in **Affiliates → Earnings** and click **Export CSV**, or call `GET /v1/affiliates/conversions/export` with the same filters. The CSV is ready to hand to bookkeeping or reconcile against a payout batch. **What happens to commission on a refund or dispute?** The related conversion moves to `reversed` and its commission is clawed back, so it no longer counts toward what you owe. If you had already marked it paid before the reversal, reconcile that overpayment with the affiliate directly. **What's the difference between owed and paid totals?** **Owed** is the sum of cleared (`approved`) commission you have not yet settled — your outstanding affiliate liability. **Paid** is the running total you have already marked paid. **Can I group conversions from a single payout run?** Yes. Pass a `batch_id` when marking paid (in the dashboard or via the API) to tag everything from one payout, which makes reconciling against your bank statement easy. ## Related Create affiliates, configure commission rates, and generate referral links. How referral tracking and commission accrual work end to end. # Set up affiliates Source: https://docs.macropay.ai/features/affiliates/setup Launch a merchant affiliate program in minutes — set commissions, share an apply link, approve partners, and attribute sales automatically. An affiliate program lets partners earn a commission for driving paid customers to you. As the merchant, you enable the program, set default commission terms, approve who can promote you, and Macropay tracks referral clicks and attributes the resulting sales. Because Macropay is your [merchant of record](/merchant-of-record/introduction), commission is always computed on the **net amount** — subtotal minus discount, excluding tax — so payouts never include money you never kept. ## 1. Enable the program and set defaults Open the **Affiliates** page in the dashboard and go to the **Program** tab. Toggle the program on and set the defaults every new affiliate inherits. | Setting | What it does | | ------------------------ | -------------------------------------------------------------------------------- | | **Commission type** | `percentage` (a share of the net sale) or `fixed` (a flat amount per sale). | | **Rate** | The commission percentage when type is `percentage` (e.g. `20` for 20%). | | **Fixed amount** | The flat commission in minor units when type is `fixed` (e.g. `2500` = \$25.00). | | **Cookie window (days)** | How long after a click a sale can still be attributed to the affiliate. | | **Recurring** | When on, the affiliate earns on renewal invoices, not just the first sale. | | **Recurring cap** | Optional limit on how many billing cycles pay out (e.g. 12 months). | | **Auto-approve** | When on, new applicants are approved instantly instead of waiting for review. | You can change these defaults at any time. Existing affiliates keep their own terms unless you edit them individually. ### Update the program via API ```bash cURL theme={null} curl -X PATCH https://api.macropay.ai/v1/affiliate-program \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -d '{ "enabled": true, "commission_type": "percentage", "rate": 20, "cookie_window_days": 30, "recurring": true, "recurring_cap": 12, "auto_approve": false }' ``` ```json Response theme={null} { "enabled": true, "commission_type": "percentage", "rate": 20, "fixed_amount": null, "cookie_window_days": 30, "recurring": true, "recurring_cap": 12, "auto_approve": false } ``` For a fixed-fee program, send `"commission_type": "fixed"` and `"fixed_amount": 2500` (in minor units) instead of `rate`. ## 2. Share your apply link Send prospective partners to your public application page. Replace `` with your organization ID: ``` https://affiliate.macropay.ai/affiliate/apply?org= ``` Affiliates submitted through this page create an application record. You can also create affiliates programmatically — the same record is produced when an applicant (or your own integration) posts to the apply endpoint: ```bash theme={null} curl -X POST https://api.macropay.ai/v1/affiliates/apply \ -H "Content-Type: application/json" \ -d '{ "org": "", "email": "partner@example.com", "name": "Partner Name", "website": "https://partner.example.com" }' ``` ### Invite affiliates by email or CSV From **Dashboard → Affiliates → Invite** you can recruit directly: Enter an email (and optional name) and we send an invite with a one-click join link. Upload a CSV with an email column (optional name) to invite many people at once — parsed in your browser, then sent. Invited people get a tokenized link (`/affiliate/apply?org=&invite=`) that prefills their email and — because you invited them — **auto-approves** them on join. Track who's pending vs accepted in the Invite tab, and resend or revoke any invite. Invites expire after 30 days. ### Get listed on the marketplace Whenever your program is **enabled**, it's automatically listed on the public **affiliate marketplace** at [affiliate.macropay.ai](https://affiliate.macropay.ai) — a searchable directory of programs (name, website, category, and commission) anyone can browse and apply to. In **Program** settings, add a short **listing description** (it falls back to your org bio) and a **category** (e.g. Design, SaaS, Development) so affiliates can search and filter to find you. ## 3. Approve applicants New applications land in the **Affiliates** tab with a `pending` status. Approve or reject each one — on approval, the affiliate is issued a unique referral **code** and inherits your program defaults. If you turned on **Auto-approve**, applicants are activated immediately and skip this step. Need a different deal for one partner? Open any affiliate and set a **per-affiliate override** for commission type, rate, fixed amount, or recurring terms. Overrides take precedence over the program defaults for that affiliate only. ## 4. How attribution works A referral link routes a visitor through Macropay's tracking endpoint before sending them to your destination URL: ``` https://api.macropay.ai/v1/affiliate/track?ref=&org=&url= ``` When a visitor opens that link, Macropay: 1. **Logs the click** against the affiliate's ``. 2. **Sets an `mp_ref` cookie** scoped to your cookie window. 3. **Redirects** to `` with `?ref=` appended so the code is available client-side. A sale is attributed when the resulting order carries the referral code in `user_metadata` as **`mp_ref`** (or `referral_code`). At `order.paid`, Macropay matches the code to an active affiliate and records the commission on the **net amount** (subtotal minus discount, excluding tax). Recurring commissions, if enabled, accrue on each subsequent paid invoice until the cap is reached. ### Getting the code onto the order There are three ways the code reaches the order — pick whichever fits your setup. **No tracking pixel on your site is required for hosted checkout.** **Automatic, no code.** Because Macropay hosts your checkout, the `mp_ref` cookie set by the tracking link is read at checkout and folded into the order for you. Works out of the box with checkout links and the hosted page. Add our snippet — it captures `?ref` and forwards `mp_ref` into Macropay checkout links automatically. Creating sessions yourself? Set `metadata.mp_ref` to the code when you create the checkout. **Snippet** — drop this on your site if you link to Macropay checkout from your own pages: ```html theme={null} ``` It reads `?ref`/`?mp_ref` from the landing URL, stores it for your cookie window, and appends `mp_ref` to any Macropay checkout links on the page. It also exposes `window.MacropayAffiliate.getRef()` if you'd rather attach the code yourself. **API** — pass the code through `metadata.mp_ref` when you create the checkout session. The value flows onto the order as `user_metadata.mp_ref`, which is what attribution matches at `order.paid`: ```bash theme={null} curl -X POST https://api.macropay.ai/v1/checkouts \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -d '{ "product_id": "", "metadata": { "mp_ref": "" } }' ``` Explicit `metadata.mp_ref` always wins — automatic injection never overwrites a code you set yourself. ## FAQ **How do I attribute a sale to an affiliate?** The order needs the affiliate's referral code in `user_metadata` as `mp_ref` (or `referral_code`). With **Macropay-hosted checkout this is automatic** — the tracking link's `mp_ref` cookie is read at checkout and attached for you. If you run your own site/checkout, add the [`affiliate.js` snippet](https://app.macropay.ai/affiliate.js) or set `metadata.mp_ref` via the API. Attribution is finalized at `order.paid`. **A percentage commission is a percentage of what amount?** The **net amount** — the subtotal minus any discount, excluding tax. Because Macropay is the merchant of record, tax is remitted on your behalf and is never part of the commission base. **Can I set a different rate for one affiliate?** Yes. Open the affiliate in the **Affiliates** tab and add a per-affiliate override for commission type, rate, fixed amount, or recurring terms. The override applies only to that affiliate and takes precedence over the program defaults. **Do affiliates earn on subscription renewals?** Only if you enable **Recurring** on the program (or as a per-affiliate override). When on, commission accrues on each paid invoice up to the optional **recurring cap**; when off, only the first paid sale earns. **How long after a click can a sale still be attributed?** For as long as your **cookie window** allows. The `mp_ref` cookie is set when the visitor passes through the tracking link, and a matching order within that window is credited to the affiliate. ## Next steps How the affiliate program fits together, end to end. Review accrued commissions and pay your partners. # Agents Source: https://docs.macropay.ai/features/agents/introduction Treat every AI agent as a billable unit — bill by usage, activity, or outcome, and prove its margin and ROI with a certified value receipt. Macropay treats an AI agent as a first-class billable unit. You register the agent once, then bill it three ways — by **usage** (LLM tokens), by **activity** (actions it takes), or by **outcome** (results it delivers) — and Macropay attributes every cost to it, computes its **agentic margin** (revenue vs. AI cost), and issues a **value receipt** that certifies the ROI behind each charge. Agent billing builds on the same meter, product, and Merchant-of-Record engine as the rest of Macropay. You get tax, dunning, and dispute handling for free — agents just add the attribution, margin, and ROI layer on top. ## What is an agent An agent is a named entity in your organization that work and money attach to. You create one with `POST /v1/agents`, giving it a `name`, an optional `external_id` (your own identifier, for idempotent linking), and an optional `description`. Each agent carries a `status`: | Status | Meaning | | ---------- | ----------------------------------------------------------- | | `active` | Billing and usage are live for this agent. | | `disabled` | Billing and usage are paused — the agent stays on record. | | `archived` | Retired from active use; history is retained for reporting. | ```bash Create an agent theme={null} curl https://api.macropay.ai/v1/agents \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -d '{ "name": "Support Copilot", "external_id": "agent_support_v2", "description": "Resolves tier-1 support tickets autonomously" }' ``` Once the agent exists, every token it burns, every action it takes, and every outcome it lands can be tagged with its ID — that attribution is what powers margin and ROI. ## The three billing models The same agent can be billed on more than one axis. Pick the model that matches how your customer perceives value. Bill on LLM tokens consumed. Best when cost scales directly with model spend and customers expect metered pricing. Bill per action the agent takes — a message sent, a tool call, a document processed. Best for predictable per-task pricing. Bill per result delivered — a resolved ticket, a booked meeting, a closed deal. Best when you sell the outcome, not the effort. ### When to use each
ModelYou bill onReach for it whenReported via
Usage Tokens (input + output) Your cost tracks the model meter and customers think in tokens or credits. AI proxy at /ai/v1
Activity Actions taken The unit of work is discrete and uniform, and value is roughly per-action. Activity signals
Outcome Results delivered You can verify the result and customers will pay for the win, not the attempt. Outcome signals
Usage billing is the natural floor (it covers your model cost), and outcome billing is the natural ceiling (it captures the value you create). Many teams run usage as COGS internally while charging the customer on outcomes — and use the [value receipt](/features/agents/value-receipts) to justify the gap. ## Building blocks Report what an agent did (activity) and achieved (outcome) via `POST /v1/signals`. These feed both activity/outcome billing and ROI. A certified ROI statement per agent — time saved, cost avoided, revenue generated, risk avoided — split into verified vs. reported value. Billed revenue minus AI cost (COGS) per agent, with a margin floor that flags agents quietly running you into the red. Drop-in Python and TypeScript instrumentation — bind an agent once, then emit signals, record tool cost, and read margin in one-liners. ### How costs get attributed Two cost sources fold into an agent's margin automatically: * **LLM cost** — when an agent's calls route through the Macropay [AI proxy](/features/llm-inference) at `/ai/v1` using an agent-bound proxy key, every request's token cost is captured and attributed with zero extra code. * **Non-LLM cost (COGS)** — tool calls, third-party APIs, and human-in-the-loop time are reported with `POST /v1/agents/{id}/costs` (or the SDK's `record_cost`), so margin reflects true cost of delivery. All of this lands in [Cost Insights](/features/cost-insights/introduction) alongside revenue, so you see real per-agent and per-customer profit — not just top-line usage. ## Get started Register the agent with `POST /v1/agents`. Keep the returned `id` (or set your own `external_id`) — it's the key everything attributes to. Route LLM calls through the [AI proxy](/features/llm-inference) with an agent-bound proxy key for automatic token cost, then emit [signals](/features/agents/signals) for activity and outcomes. To bill on activity or outcome, provision a signal-scoped meter and price with `POST /v1/agents/billing-models`. ```bash Report an outcome theme={null} curl https://api.macropay.ai/v1/signals \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -d '{ "signals": [{ "event_name": "meeting_booked", "kind": "outcome", "agent_id": "", "external_customer_id": "acct_8842", "data": { "value_cents": 20000 }, "external_id": "mtg_2026_0603_001" }] }' ``` Read agentic margin with `GET /v1/agents/{id}/margin` and the certified ROI with `GET /v1/agents/{id}/value-receipt`, or explore both in the Macropay dashboard. ```bash Read the value receipt theme={null} curl "https://api.macropay.ai/v1/agents//value-receipt" \ -H "Authorization: Bearer " ``` Signals require a customer (`customer_id` or `external_customer_id`) — billing always rolls up to an account. Outcomes are idempotent on `external_id`, so retries won't double-count. ## FAQ **When should I bill on outcomes vs. tokens?** Bill on tokens when your cost scales with model spend and the customer reasons in usage — it's the safest floor because it always covers COGS. Bill on outcomes when you can verify the result (a booked meeting, a resolved ticket, a closed deal) and the customer is buying that result rather than the effort behind it. Outcome pricing captures the value you create instead of just your cost; the [value receipt](/features/agents/value-receipts) is what lets you defend the higher price. **Do agents require the AI proxy?** No. The [AI proxy](/features/llm-inference) at `/ai/v1` is the easiest way to capture LLM cost — point an agent-bound proxy key at it and token cost is attributed automatically. But you can run agent billing without it: report non-LLM COGS with `POST /v1/agents/{id}/costs` and bill on activity or outcome [signals](/features/agents/signals) directly. The proxy is recommended, not mandatory. **How is agentic margin computed?** Margin is billed revenue minus AI cost (COGS) for an agent over a period, returned by `GET /v1/agents/{id}/margin` as `revenue_cents`, `cost_cents`, `margin_cents`, and `margin_pct`, broken down `by_model`. Revenue comes from the agent's metered charges; cost comes from proxy token spend plus any recorded non-LLM COGS. A `low_margin` flag trips when `margin_pct` falls below the configured `margin_floor_pct`. See [Agentic margin](/features/agents/margin). **What is a value receipt?** A value receipt is a certified ROI statement Macropay computes per agent from its signals and cost — covering human-value-equivalent, time saved, cost savings, revenue generated, and risk avoided. You supply the assumptions (e.g. minutes saved per action, an hourly rate) and the engine produces the numbers, splitting outcome value into verified (confirmed by a trusted source) and reported (still self-attested). See [Value receipts](/features/agents/value-receipts). **Can one agent use more than one billing model?** Yes. A single agent can bill usage on tokens, activity on actions, and outcomes on results at the same time — each axis is just a different meter and price scoped to the same `agent_id`. Margin and ROI net all of them together. ## Next steps Resell models through one URL and capture token cost automatically. See true per-agent and per-customer margin once cost sits next to revenue. Wire a meter, attach a price, and turn usage into invoices end-to-end. See how agent products put usage, activity, and outcome billing together. # Agentic Margin Source: https://docs.macropay.ai/features/agents/margin See revenue vs. AI cost (COGS) per agent so you know which agents make money — captured automatically from every proxied model call. Agentic margin is what each AI agent earns after the cost of running it. Macropay joins the revenue you bill against the model and tool spend it took to deliver, so you can tell a profitable agent from one quietly burning cash — per agent or across your whole organization. Both sides of the equation are captured for you. When you route model calls through the [AI proxy](/features/llm-inference) (`/ai/v1`), every request writes an `ai.completion` event carrying the billed amount (revenue) and the upstream provider cost (COGS). Macropay rolls those up into a `MarginSummary` — no metering code, no manual joins. Revenue here is the amount you bill the end customer (the proxy's marked-up rate). Cost is what the upstream provider charged you. The difference is your margin, sliced by model and flagged when it drops below a floor. ## Where the numbers come from Each proxied call records billed amount and upstream model cost on the same `ai.completion` event. Margin needs no extra reporting from you. Tool calls, third-party APIs, and human-in-the-loop time can be recorded as `agent.cost` events that reduce margin — see below. Every summary includes `by_model[]`, so you can see exactly which model is eating your spread. A `low_margin` flag trips the moment an agent's margin falls under your floor (default 20%), so thin or negative agents surface on their own. ## Read an agent's margin Two endpoints, same `MarginSummary` shape. Both accept optional `since` and `until` ISO-8601 query params to scope a window. | Endpoint | Returns | | ---------------------------- | ----------------------------- | | `GET /v1/agents/{id}/margin` | Margin for a single agent | | `GET /v1/agents/margin` | Org rollup across every agent | ```bash Single agent theme={null} curl https://api.macropay.ai/v1/agents/agt_42/margin \ -H "Authorization: Bearer " ``` ```bash Org rollup theme={null} curl "https://api.macropay.ai/v1/agents/margin?since=2026-05-01T00:00:00Z" \ -H "Authorization: Bearer " ``` ### Response: `MarginSummary` ```json theme={null} { "agent_id": "agt_42", "revenue_cents": 124050.0, "cost_cents": 38200.0, "margin_cents": 85850.0, "margin_pct": 69.21, "low_margin": false, "margin_floor_pct": 20.0, "by_model": [ { "model": "claude-sonnet-4", "revenue_cents": 98000.0, "cost_cents": 28000.0, "margin_cents": 70000.0, "margin_pct": 71.43, "count": 1820 }, { "model": "gpt-4o-mini", "revenue_cents": 26050.0, "cost_cents": 10200.0, "margin_cents": 15850.0, "margin_pct": 60.84, "count": 940 } ] } ``` | Field | Meaning | | ------------------ | ----------------------------------------------------------------------------- | | `revenue_cents` | Total billed to customers (your marked-up rate) | | `cost_cents` | Total COGS — upstream model spend plus any recorded `agent.cost` | | `margin_cents` | `revenue_cents − cost_cents` | | `margin_pct` | Margin as a percent of revenue; `0` when there's no revenue | | `low_margin` | `true` when there is revenue **and** `margin_pct` is below `margin_floor_pct` | | `margin_floor_pct` | The guardrail threshold (default `20`) | | `by_model[]` | The same metrics broken out per model, sorted by revenue | ## The low-margin guardrail `low_margin` is the single signal you alert on. It is `true` only when an agent has earned revenue *and* its `margin_pct` sits under `margin_floor_pct` — so an agent with no billed activity never trips it, and a profitable agent stays quiet. The floor defaults to **20%**. Wire it into monitoring: poll the org rollup on a schedule, and if `low_margin` is `true` or any entry in `by_model[]` shows a thin spread, you've caught a pricing or model-choice problem before it shows up on a P\&L. A negative `margin_cents` means the agent is losing money on every run — usually a sign the upstream model costs more than you're charging. A negative margin doesn't stop requests. Margin is reporting, not a budget cap. To hard-stop spend, set a **budget limit** on the proxy key — see [LLM inference](/features/llm-inference) — which returns `403` once the ceiling is hit. ## Record non-LLM costs LLM spend is captured automatically, but agents also cost money in ways the proxy never sees: a paid search API, a geocoding lookup, a human reviewer. Report those as COGS with `POST /v1/agents/{id}/costs` and they fold straight into the agent's margin as an `agent.cost` event — adding cost without adding revenue. A `customer_id` or `external_customer_id` is required so the cost attributes to the right account. Pass `external_id` as an idempotency key — it dedupes on `(organization, external_id)`, so retries never double-count. ```bash theme={null} curl https://api.macropay.ai/v1/agents/agt_42/costs \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -d '{ "amount_cents": 250, "currency": "usd", "description": "serp_api", "external_customer_id": "acct_8842", "external_id": "run_91f3-serp" }' ``` ```json theme={null} { "status": "recorded", "agent_id": "agt_42" } ``` | Field | Required | Notes | | -------------------------------------- | -------- | -------------------------------------------------------------------------------- | | `amount_cents` | Yes | The cost incurred, in cents. Must be greater than 0. | | `currency` | No | ISO currency code; defaults to `usd`. | | `description` | No | What the cost was for, e.g. `serp_api`. Surfaces as the "model" in `by_model[]`. | | `customer_id` / `external_customer_id` | One of | Attributes the cost to a customer. | | `external_id` | No | Idempotency key — dedupes on `(org, external_id)`. | Using the Macropay SDK? `cost.record(...)` calls this endpoint for you, and the tool-cost helpers can capture vendor spend automatically as your agent runs — no manual POST per tool call. ## FAQ **How is margin calculated?** Margin is billed revenue minus AI cost (COGS). Revenue and upstream model cost both come from the `ai.completion` events the proxy records on every call; any `agent.cost` events you report add to the cost side. Macropay sums them into `revenue_cents`, `cost_cents`, and `margin_cents`, with `margin_pct` as margin over revenue. The breakdown per model lives in `by_model[]`. **How do I record non-LLM costs?** Call `POST /v1/agents/{id}/costs` with `amount_cents`, a `currency`, an optional `description`, a `customer_id` or `external_customer_id`, and an `external_id` for idempotency. It's stored as an `agent.cost` event that reduces margin without adding revenue. The SDK's `cost.record()` wraps this endpoint. **What triggers the low-margin flag?** `low_margin` is `true` when an agent has earned revenue and its `margin_pct` is below `margin_floor_pct` (default 20%). An agent with no billed revenue never trips the flag, even if it has recorded costs. **Is margin a spending limit?** No. Margin is reporting only — it never blocks a request. To cap spend, set a budget limit on the proxy key in [LLM inference](/features/llm-inference); the proxy returns `403` once the ceiling is reached. **How does this relate to Cost Insights?** Agentic margin is the agent-scoped view of the same revenue-vs-cost ledger. For per-customer profit, LTV, and cost-annotated events beyond agents, see [Cost Insights](/features/cost-insights/introduction). ## Next steps Send model traffic through `/ai/v1` so revenue and cost are captured for you. See true profit, margin, and LTV per customer across your whole business. # Agent SDK Source: https://docs.macropay.ai/features/agents/sdk Instrument an AI agent in a few lines of Python or TypeScript — route LLM calls through the proxy, report outcomes, and bill on results. The Agent SDK is the drop-in instrumentation layer for agent billing: point your existing OpenAI client at the Macropay proxy to capture model cost automatically, then report what the agent did and achieved so margin and ROI compute themselves. It ships for both **Python** (`macropay`) and **TypeScript** (`@macropay/sdk`) with a matching API. Instrumentation does three things: **captures LLM cost** (via the proxy base URL), **reports signals** (activity + outcome), and **records non-LLM cost** (tools, third-party APIs, human-in-the-loop). Those three feeds power [Agentic Margin](/features/agents/margin) and [value receipts](/features/agents/value-receipts). ## Install ```bash Python theme={null} pip install macropay ``` ```bash TypeScript theme={null} npm install @macropay/sdk openai ``` ## Capture LLM cost with one base URL The Macropay AI proxy is OpenAI-compatible and mounted at `/ai/v1`. Point any OpenAI client at it, authenticate with an **agent-bound proxy key**, and every call's cost is attributed to that agent — no wrapper code. The SDK exposes a helper so you never hand-write the URL. ```python Python theme={null} from openai import OpenAI from macropay.agents import openai_base_url oai = OpenAI( api_key="macropay_proxy_...", # agent-bound proxy key base_url=openai_base_url(), # https://api.macropay.ai/ai/v1 ) ``` ```ts TypeScript theme={null} import OpenAI from "openai"; import { openaiBaseUrl } from "@macropay/sdk"; const oai = new OpenAI({ apiKey: "macropay_proxy_...", // agent-bound proxy key baseURL: openaiBaseUrl(), // https://api.macropay.ai/ai/v1 }); ``` `openai_base_url()` / `openaiBaseUrl()` default to production. Pass `sandbox=True` (Python) or `{ sandbox: true }` (TypeScript) to target the sandbox, or pass an explicit base URL to override. See [LLM inference](/features/llm-inference) for the full proxy reference. ## AgentInstrumentation `AgentInstrumentation` binds one agent (and optionally one customer) once, so every signal and cost call is a one-liner. It wraps the low-level Macropay client. ```python theme={null} from macropay import Macropay from macropay.agents import AgentInstrumentation client = Macropay(api_key="macropay_sk_...") agent = AgentInstrumentation( client, agent_id="agt_sdr", customer_id="cus_123", # or external_customer_id="acme-co" ) ``` | Method | Purpose | | ------------------------------------------------------------------------------------ | ------------------------------------------------------------------------- | | `activity(event_name, data=None, external_id=None)` | Report an action the agent took (`message_sent`, `tool_call`). | | `outcome(event_name, value_cents=None, verified=False, data=None, external_id=None)` | Report a result (`meeting_booked`, `deal_won`). `value_cents` drives ROI. | | `record_cost(amount_cents, description=None, currency="usd", external_id=None)` | Record non-LLM COGS so margin reflects true cost. | | `margin(**params)` | Fetch this agent's current Agentic Margin. | | `value_receipt(**assumptions)` | Fetch this agent's ROI value receipt. | | `track_tool(name, cost_cents=...)` | Async context manager — auto-records cost **on success only**. | | `track_cost(name, cost_cents=...)` | Decorator — auto-records cost **on success only**. | All methods are `async`. Pass `external_id` to dedupe — outcomes are idempotent on it. ```ts theme={null} import { Macropay, AgentInstrumentation } from "@macropay/sdk"; const client = new Macropay({ apiKey: "macropay_sk_..." }); const agent = new AgentInstrumentation(client, "agt_sdr", { customerId: "cus_123", // or externalCustomerId: "acme-co" }); ``` | Method | Purpose | | ---------------------------------------------------------------- | ------------------------------------------------------- | | `activity(eventName, { data, externalId })` | Report an action the agent took. | | `outcome(eventName, { valueCents, verified, data, externalId })` | Report a result; `valueCents` drives ROI. | | `recordCost(amountCents, { description, currency, externalId })` | Record non-LLM COGS. | | `setupBilling({ kind, pricePerUnitCents, ... })` | Provision a meter + product for this agent in one call. | | `margin(params)` | Fetch this agent's current Agentic Margin. | | `valueReceipt(params)` | Fetch this agent's ROI value receipt. | All methods return promises. Pass `externalId` to dedupe — outcomes are idempotent on it. ## Report what the agent did and achieved [Signals](/features/agents/signals) are the heart of agent billing. **Activity** signals are what the agent *did*; **outcome** signals are what it *achieved* — and an outcome carries the value it produced. ```python Python theme={null} # Something the agent did await agent.activity("message_sent", data={"channel": "email"}) # Something the agent achieved, worth $200, confirmed by your CRM await agent.outcome( "meeting_booked", value_cents=20000, verified=True, external_id="hubspot-deal-8841", # idempotent dedupe key ) ``` ```ts TypeScript theme={null} // Something the agent did await agent.activity("message_sent", { data: { channel: "email" } }); // Something the agent achieved, worth $200, confirmed by your CRM await agent.outcome("meeting_booked", { valueCents: 20000, verified: true, externalId: "hubspot-deal-8841", // idempotent dedupe key }); ``` Set `verified` when an outcome is confirmed by a trusted source — a CRM webhook, a payment, a signed contract — rather than self-reported by the agent. The [value receipt](/features/agents/value-receipts) then splits **verified** from **unverified** value, exposing the trust gap to your customer. ## Record non-LLM cost LLM cost is captured automatically through the proxy. Everything else — search APIs, enrichment, human review — you report with `record_cost` so [margin](/features/agents/margin) reflects true COGS. ```python Python theme={null} await agent.record_cost(50, description="serp_api") # 50 cents ``` ```ts TypeScript theme={null} await agent.recordCost(50, { description: "serp_api" }); // 50 cents ``` ### Automatic tool-cost capture (Python) `track_tool` (an async context manager) and `track_cost` (a decorator) record the cost **and** emit a `tool.` activity signal — but **only when the call succeeds**. A tool call that raises was a failure you weren't charged for, so it never touches margin. ```python theme={null} # As a context manager async with agent.track_tool("serp_search", cost_cents=2): results = await serp.search(query) # As a decorator — cost_cents can be a number or a callable for usage pricing @agent.track_cost("enrich", cost_cents=lambda rows: len(rows) * 1) async def enrich(rows: list[dict]) -> list[dict]: ... ``` ## End-to-end: route a call, post an outcome A minimal SDR-style agent: the LLM call goes through the proxy (cost captured), then the booked meeting is reported as a verified outcome worth \$200. ```python Python theme={null} from openai import OpenAI from macropay import Macropay from macropay.agents import openai_base_url, AgentInstrumentation # 1. LLM client routed through the proxy → cost auto-attributed to the agent oai = OpenAI(api_key="macropay_proxy_...", base_url=openai_base_url()) # 2. Instrumentation bound to the agent + customer client = Macropay(api_key="macropay_sk_...") agent = AgentInstrumentation(client, "agt_sdr", customer_id="cus_123") # 3. Do the work reply = oai.chat.completions.create( model="gpt-4o-mini", messages=[{"role": "user", "content": "Draft a meeting request for Acme."}], ) await agent.activity("message_sent", data={"channel": "email"}) # 4. Report the result — this is what you bill and prove ROI on await agent.outcome( "meeting_booked", value_cents=20000, verified=True, external_id="hubspot-deal-8841", ) ``` ```ts TypeScript theme={null} import OpenAI from "openai"; import { Macropay, openaiBaseUrl, AgentInstrumentation } from "@macropay/sdk"; // 1. LLM client routed through the proxy → cost auto-attributed to the agent const oai = new OpenAI({ apiKey: "macropay_proxy_...", baseURL: openaiBaseUrl() }); // 2. Instrumentation bound to the agent + customer const client = new Macropay({ apiKey: "macropay_sk_..." }); const agent = new AgentInstrumentation(client, "agt_sdr", { customerId: "cus_123" }); // 3. Do the work const reply = await oai.chat.completions.create({ model: "gpt-4o-mini", messages: [{ role: "user", content: "Draft a meeting request for Acme." }], }); await agent.activity("message_sent", { data: { channel: "email" } }); // 4. Report the result — this is what you bill and prove ROI on await agent.outcome("meeting_booked", { valueCents: 20000, verified: true, externalId: "hubspot-deal-8841", }); ``` ## Close the billing loop in one call `setup_billing` / `setupBilling` provisions a signal-scoped meter for the agent **and** a product with a `metered_unit` price — so you skip the manual meter to price to product wiring. Bill per activity or per outcome. ```python Python theme={null} await agent.setup_billing( kind="outcome", signal_name="meeting_booked", price_per_unit_cents=500, # $5 per booked meeting product_name="SDR — per meeting", ) ``` ```ts TypeScript theme={null} await agent.setupBilling({ kind: "outcome", signalName: "meeting_booked", pricePerUnitCents: 500, // $5 per booked meeting productName: "SDR — per meeting", }); ``` Outcome-based pricing pairs naturally with verified outcomes: charge only for results your CRM confirmed. See [outcome-based billing in practice](/guides/ai-billing) for the full pattern. ## FAQ **Do I need the SDK?** No. The SDK is a thin convenience layer over the REST API — you can route LLM calls through the [proxy](/features/llm-inference) and post [signals](/features/agents/signals) with plain HTTP. The SDK just makes cost capture, signal reporting, and billing setup one-liners in Python and TypeScript. **How do I bill by outcome with the SDK?** Report results with `outcome(event_name, value_cents=...)`, then create an outcome meter and price. `setup_billing(kind="outcome", signal_name=..., price_per_unit_cents=...)` does both in one call, so each reported outcome bills your customer automatically. Mark outcomes `verified` to bill only on results a trusted source confirmed. **Which languages are supported?** Python (the `macropay` package) and TypeScript (`@macropay/sdk`) have full parity for agent instrumentation. Both expose `openai_base_url` / `openaiBaseUrl` and `AgentInstrumentation`. Any other language can use the [REST API](/api-reference) directly. **Does the proxy add latency?** The proxy forwards your request to the upstream provider and streams the response back; metering happens on the response, not in the critical path. You also get real-time usage and budget headers on every call — see [LLM inference](/features/llm-inference). **How is the LLM cost attributed to the right agent?** Through the proxy key. Mint a proxy key bound to the agent, set it as the OpenAI client's `api_key`, and every call through `openai_base_url()` records its token cost against that agent — feeding [Agentic Margin](/features/agents/margin) with zero extra code. # Signals API Source: https://docs.macropay.ai/features/agents/signals Report what an AI agent did and achieved. Ingest activity and outcome signals to bill on results and prove agent value. The Signals API is how an AI agent tells Macropay what it did and what it achieved. You `POST /v1/signals` with one or more signals; each becomes an event named `activity.` or `outcome.`, attributed to a specific agent — so you can bill on results and read each agent's value. ## Activity vs. outcome Every signal has a `kind`. The distinction drives both how you read agent performance and how you bill. An action the agent took — `message_sent`, `lead_enriched`, `document_processed`. Activities measure effort and throughput. They are not, on their own, billable results. A billable result the agent produced — `meeting_booked`, `ticket_resolved`, `revenue_attributed`. Outcomes carry a `data.value_cents` and an optional `data.verified` flag, and they feed the value receipt. A signal of kind `activity` named `message_sent` is recorded as the event `activity.message_sent`. A signal of kind `outcome` named `meeting_booked` is recorded as `outcome.meeting_booked`. Both are attributed to the `agent_id` you pass, so usage-based meters and per-agent reporting can filter on the agent that produced the work. ## Request shape `POST https://api.macropay.ai/v1/signals` accepts a batch. Send as many signals as you like in one call. ```json theme={null} { "organization_id": "org_8af21c", "signals": [ { "event_name": "message_sent", "kind": "activity", "agent_id": "agt_sdr_01", "external_customer_id": "cus_acme_42", "external_id": "msg_2024-06-03T14:22:08Z_8f3a", "data": { "channel": "email", "thread_id": "th_91c2" } }, { "event_name": "meeting_booked", "kind": "outcome", "agent_id": "agt_sdr_01", "external_customer_id": "cus_acme_42", "external_id": "mtg_a1b2c3d4", "data": { "value_cents": 5000, "verified": false, "meeting_at": "2026-06-10T15:00:00Z" } } ] } ``` | Field | Required | Description | | ------------------------------------------- | ----------- | ------------------------------------------------------------------------------------------------------------------------------ | | `event_name` | Yes | The signal name. Mapped to `activity.` or `outcome.`. | | `kind` | Yes | `"activity"` or `"outcome"`. | | `agent_id` | Yes | The agent the signal is attributed to. | | `customer_id` **or** `external_customer_id` | Yes | One of the two is required. Use `external_customer_id` to reference your own customer key. | | `external_id` | Recommended | Your unique idempotency key for the signal. Required in practice for outcomes — see [dedupe](#dedupe-outcomes-by-external_id). | | `data` | No | Free-form JSON. For outcomes, set `data.value_cents` and optionally `data.verified`. | A `customer_id` or `external_customer_id` is required on every signal — Macropay attributes the activity and value to a customer as well as an agent. Send one or the other, not both. ## Post an activity and an outcome The example below reports one activity (`message_sent`) and one outcome (`meeting_booked`) in a single batch. ```bash cURL theme={null} curl -X POST https://api.macropay.ai/v1/signals \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -d '{ "organization_id": "org_8af21c", "signals": [ { "event_name": "message_sent", "kind": "activity", "agent_id": "agt_sdr_01", "external_customer_id": "cus_acme_42", "external_id": "msg_2024-06-03T14:22:08Z_8f3a", "data": { "channel": "email" } }, { "event_name": "meeting_booked", "kind": "outcome", "agent_id": "agt_sdr_01", "external_customer_id": "cus_acme_42", "external_id": "mtg_a1b2c3d4", "data": { "value_cents": 5000, "verified": false } } ] }' ``` ```python Python theme={null} import os, requests requests.post( "https://api.macropay.ai/v1/signals", headers={"Authorization": f"Bearer {os.environ['MACROPAY_ACCESS_TOKEN']}"}, json={ "organization_id": "org_8af21c", "signals": [ { "event_name": "message_sent", "kind": "activity", "agent_id": "agt_sdr_01", "external_customer_id": "cus_acme_42", "external_id": "msg_2024-06-03T14:22:08Z_8f3a", "data": {"channel": "email"}, }, { "event_name": "meeting_booked", "kind": "outcome", "agent_id": "agt_sdr_01", "external_customer_id": "cus_acme_42", "external_id": "mtg_a1b2c3d4", "data": {"value_cents": 5000, "verified": False}, }, ], }, ) ``` ## Dedupe outcomes by external\_id Outcomes are deduplicated by the pair `(organization, external_id)`. The first signal with a given `external_id` is recorded; later signals carrying the same `external_id` are ignored. This makes the endpoint safe to retry and protects you from double-billing. Always set a stable, unique `external_id` on every outcome — derive it from the thing that happened (a meeting ID, a resolved ticket ID, an order ID), not from a timestamp or a random value generated per call. If you regenerate the `external_id` on retry, the same outcome will be counted twice. Activities can share the dedupe behavior too, but the high-value guarantee is for outcomes, because outcomes are what get billed. ## Outcomes drive the value receipt For an `outcome` signal, two fields in `data` matter most: * **`data.value_cents`** — the monetary value of the result, in cents. This is the amount attributed to the agent and rolled up into its [value receipt](/features/agents/value-receipts). * **`data.verified`** — an optional boolean. Set it `true` once the value is confirmed (the meeting actually happened, the revenue actually landed). Verified value is reported separately from reported value, so you can bill on confirmed results and watch the gap between what an agent claimed and what stuck. Outcome value and the `verified` flag flow straight into the agent's value receipt, where reported value and verified value are split. To turn outcomes into invoices, point a usage-based [meter](/features/usage-based-billing/introduction) at the relevant `outcome.` event and attach a price. Reporting a signal never bills a customer on its own. Signals are recorded as events; a [meter](/features/usage-based-billing/introduction) aggregates the matching events and a price turns the aggregate into an amount. Macropay then bills as the merchant of record, handling sales tax and VAT for you. ## FAQ **What is the difference between activity and outcome?** An activity is an action the agent took (for example `message_sent`), recorded as `activity.`. An outcome is a billable result the agent produced (for example `meeting_booked`), recorded as `outcome.` and carrying `data.value_cents`. Activities measure effort; outcomes measure value and are what you bill on. **How do I avoid double-billing an outcome?** Set a stable, unique `external_id` on each outcome, derived from the underlying event (a meeting ID or order ID), not a timestamp or per-call random value. Outcomes dedupe by `(organization, external_id)`, so the first signal wins and any retry with the same `external_id` is ignored — making the endpoint safe to call repeatedly. **What is verified value?** Verified value is outcome value you've confirmed actually occurred, marked with `data.verified: true`. Macropay reports verified value separately from reported (unverified) value on the agent's value receipt, so you can bill only on confirmed results and measure the gap between what an agent claimed and what was real. ## Next steps See how reported and verified outcome value rolls up per agent. Aggregate `outcome.` events and attach a price to bill on results. # Value Receipts Source: https://docs.macropay.ai/features/agents/value-receipts Generate a certified ROI statement for any agent — human value equivalent, time saved, cost savings, revenue, and risk avoided, with a verified-value split. A **value receipt** is a certified ROI statement for one agent over a date range. Call `GET /v1/agents/{id}/value-receipt` and Macropay turns the agent's logged [signals](/features/agents/signals) into five dollar figures — Human Value Equivalent, Time Saved, Cost Savings, Revenue Generated, and Risk Avoided — plus a Total Value, and splits that total into verified versus unverified value based on your outcome data. It is the number you put in front of a customer, a CFO, or a renewal conversation: "this agent returned $X this month, and $Y of it is independently verified." A value receipt does not move money. It is a read-only computation over your signals — pair it with [margin](/features/agents/margin) to see what the agent *cost* you, and you have both sides of the ROI ledger. ## What the receipt computes Every figure is derived from two inputs: the count of **activity signals** in the window (`actions`) and a set of **assumptions** (minutes saved per action, a loaded hourly rate, and so on). Revenue is the exception — it comes straight from your **outcome** signals. | Figure | Formula | | -------------------------- | ------------------------------------------------------------------------ | | **Human Value Equivalent** | `actions × (minutes_per_action / 60) × hourly_rate_cents` | | **Time Saved (hours)** | `actions × minutes_per_action / 60` | | **Cost Savings** | `actions × cost_avoided_per_action_cents` | | **Revenue Generated** | sum of `value_cents` across outcome signals | | **Risk Avoided** | `actions × risk_value_per_action_cents` | | **Total Value** | Human Value Equivalent + Cost Savings + Revenue Generated + Risk Avoided | `actions` is the **activity signal count** in the window. Time Saved is reported as hours for readability; it is not added into Total Value (it is the same labor already priced in Human Value Equivalent). ### Verified vs unverified value Outcome signals carry a `data.verified` flag. When an outcome was confirmed against your system of record — a closed deal, a paid invoice, a resolved ticket — it is **verified**. The receipt sums verified outcomes into `verified_value_cents` and the rest into `unverified_value_cents`, so the reader can trust the hard number and discount the soft one. Outcome value backed by a `data.verified: true` signal. This is the figure that survives audit. Outcome value still self-reported by the agent and not yet confirmed. Useful for projections, weaker for proof. ## Assumptions and precedence The labor, cost, and risk figures depend on assumptions you control. Each one resolves with a clear precedence: A value passed on the request — `?hourly_rate_cents=7500` — wins for that call. Use it for one-off "what if our rate were higher" scenarios. If no query param is given, Macropay falls back to the assumptions saved on the agent (`roi_assumptions`). Set these once so every receipt for the agent is consistent. If neither is set, the documented default applies. | Assumption | Default | Meaning | | ------------------------------- | --------------- | ------------------------------------------ | | `minutes_per_action` | `5` | Minutes a human would spend per action | | `hourly_rate_cents` | `5000` (\$50/h) | Fully loaded labor rate | | `cost_avoided_per_action_cents` | `0` | Direct cost the agent removes per action | | `risk_value_per_action_cents` | `0` | Risk/compliance value protected per action | Leave `cost_avoided_per_action_cents` and `risk_value_per_action_cents` at `0` until you have a defensible number — Total Value stays conservative and credible. Raise them once finance signs off on the assumption. ## Request a receipt Pass a date range with `start` and `end`, and override any assumptions inline. Authenticate with a secret key. ```bash curl theme={null} curl -G "https://api.macropay.ai/v1/agents/agt_4f2c91/value-receipt" \ -H "Authorization: Bearer " \ --data-urlencode "start=2026-05-01" \ --data-urlencode "end=2026-05-31" \ --data-urlencode "minutes_per_action=8" \ --data-urlencode "hourly_rate_cents=7500" \ --data-urlencode "cost_avoided_per_action_cents=120" \ --data-urlencode "risk_value_per_action_cents=300" ``` ```python Python theme={null} import httpx resp = httpx.get( "https://api.macropay.ai/v1/agents/agt_4f2c91/value-receipt", headers={"Authorization": "Bearer "}, params={ "start": "2026-05-01", "end": "2026-05-31", "minutes_per_action": 8, "hourly_rate_cents": 7500, "cost_avoided_per_action_cents": 120, "risk_value_per_action_cents": 300, }, ) print(resp.json()) ``` ```ts TypeScript theme={null} const params = new URLSearchParams({ start: '2026-05-01', end: '2026-05-31', minutes_per_action: '8', hourly_rate_cents: '7500', cost_avoided_per_action_cents: '120', risk_value_per_action_cents: '300', }) const resp = await fetch( `https://api.macropay.ai/v1/agents/agt_4f2c91/value-receipt?${params}`, { headers: { Authorization: 'Bearer ' } }, ) const receipt = await resp.json() ``` ### Sample response ```json theme={null} { "agent_id": "agt_4f2c91", "period": { "start": "2026-05-01", "end": "2026-05-31" }, "actions": 1240, "assumptions": { "minutes_per_action": 8, "hourly_rate_cents": 7500, "cost_avoided_per_action_cents": 120, "risk_value_per_action_cents": 300 }, "human_value_equivalent_cents": 1240000, "time_saved_hours": 165.33, "cost_savings_cents": 148800, "revenue_generated_cents": 920000, "risk_avoided_cents": 372000, "total_value_cents": 2680800, "verified_value_cents": 2280800, "unverified_value_cents": 400000, "currency": "usd" } ``` In the sample, `human_value_equivalent_cents` is `1240 × (8 / 60) × 7500 ≈ 1,240,000`. `total_value_cents` sums human value, cost savings, revenue, and risk avoided — `1,240,000 + 148,800 + 920,000 + 372,000 = 2,680,800`. Of that, `2,280,800` is verified; the remaining `400,000` of revenue is still self-reported. ## FAQ **Where do the numbers come from?** From the agent's logged [signals](/features/agents/signals). `actions` is the count of activity signals in the window; Revenue Generated is the summed `value_cents` of outcome signals. The labor, cost, and risk figures multiply that action count by your assumptions. Nothing is estimated by Macropay beyond the formulas above. **How do I set my own assumptions?** Three ways, in order of precedence: pass them as query params for a single call (highest), save them on the agent as `roi_assumptions` so every receipt uses them, or omit them and Macropay applies the defaults (`minutes_per_action=5`, `hourly_rate_cents=5000`, the rest `0`). A query param always overrides the stored value, which always overrides the default. **What makes value verified?** An outcome signal with `data.verified: true` — meaning the outcome was confirmed against your system of record rather than self-reported by the agent. Verified outcomes roll into `verified_value_cents`; everything else lands in `unverified_value_cents`. The split lets you show a hard, audit-ready number alongside an optimistic projection. **Does requesting a receipt charge anyone?** No. The endpoint is a read-only computation over existing signals. To see the cost side of ROI, use [margin](/features/agents/margin). **Can I change the date range?** Yes — `start` and `end` define the window, and `actions`, revenue, and every derived figure are recomputed for exactly that period. # Analytics Source: https://docs.macropay.ai/features/analytics A zero-setup dashboard for revenue, conversion, cost, and agent margin — MRR and AI COGS computed for you, no BI tool or SQL required. Most teams treat analytics as a second project: pipe events somewhere, model them, build charts, keep them in sync. Macropay skips all of that. The moment you take your first payment, your dashboard already knows your revenue, your subscription base, your checkout funnel, and — if you're billing for AI — what each customer costs you to serve. And because Macropay is the [merchant of record](/merchant-of-record/introduction), the figures are already clean. Sales tax and VAT are collected and remitted on your behalf, disputes and refunds are reconciled for you, so what you see is the business you actually operate — not a gross number you still have to net down by hand. Looking for a metric we don't expose yet? [Let us know what would help](mailto:support@macropay.ai) and we'll consider adding it. ## Scope every chart with three controls The whole dashboard responds to the same three filters. Set them once and every chart on the page redraws. | Control | Effect | | ------------- | ------------------------------------------------------------------------- | | **Period** | Chooses the X-axis bucket — hourly, daily, weekly, monthly, or yearly. | | **Timeframe** | Sets the date range the dashboard reports over. | | **Product** | Combines all products by default; pick one product or tier to isolate it. | Mix them to answer specific questions. Want to know whether your *Scale* tier is accelerating without the rest of the catalog drowning it out? Set the period to *weekly*, the timeframe to *last 90 days*, and the product filter to *Scale*. ## Revenue and the funnel behind it These metrics cover money earned and the checkout flow that produces it. | Metric | Definition | | ----------------------------------- | -------------------------------------------------- | | **Revenue** | Gross revenue earned, before fees. | | **Orders** | Count of product sales and subscription payments. | | **Average Order Value (AOV)** | Revenue ÷ orders. | | **One-Time Products** | Number of one-time products sold. | | **One-Time Products Revenue** | Revenue from one-time product sales. | | **New Subscriptions** | Subscriptions started in the period. | | **New Subscription Revenue** | Revenue from those new subscriptions. | | **Renewed Subscriptions** | Subscriptions that renewed in the period. | | **Renewed Subscription Revenue** | Revenue from renewals. | | **Active Subscriptions** | New + renewed subscriptions currently active. | | **Monthly Recurring Revenue (MRR)** | Recurring revenue across all active subscriptions. | | **Checkouts** | Number of checkouts created. | | **Succeeded Checkouts** | Checkouts that became an order or subscription. | | **Checkouts Conversion Rate** | Succeeded checkouts ÷ checkouts created. | Revenue is reported **before** Macropay's fees and gross of sales tax/VAT. As merchant of record we collect and remit that tax for you — it never enters your revenue line, and it never becomes your liability. ## Cost and margin, side by side with revenue Profitability shouldn't live in a separate spreadsheet. Macropay tracks the cost of serving each product alongside what you charged for it, so margin is something you read off the same page as revenue. For AI and agent products — where the cost of a sale is real model spend that moves with usage — this is where the dashboard earns its keep. | Metric | Definition | | -------------------------------- | --------------------------------- | | **Costs** | Total costs incurred. | | **Cumulative Costs** | Running total of costs over time. | | **Cost Per User** | Average cost per active user. | | **Gross Margin** | Revenue − costs. | | **Gross Margin Percentage** | Gross margin ÷ revenue. | | **Net Cashflow** | Revenue − costs. | | **Monthly Recurring Cost (MRC)** | Costs ÷ active subscriptions. | | **Return on Investment (ROI)** | (Revenue − costs) ÷ costs. | ### Agentic margin for AI products Bill with [usage-based pricing](/features/usage-based-billing/introduction) or run [agent billing](/features/usage-based-billing/introduction) and these cost metrics become your **agentic margin**: the revenue you booked measured against the AI cost of goods sold for each agent. You don't have to report any of that spend by hand — Macropay captures it as it happens: * The [AI proxy](/features/llm-inference) at `/ai/v1` records token cost on every request it forwards, so the model spend behind each call is attributed automatically. * Cost events and cost traces tie that spend back to the customer or agent that triggered it, feeding **Gross Margin** and **Cost Per User** with real numbers. The payoff is early warning. When a power user runs a long-context agent loop a hundred times a day on a flat-rate plan, their serving cost climbs while their price doesn't — and you see the margin compress on the dashboard before it quietly erodes the quarter. Pair the **Product** filter with the cost metrics to compare margin tier by tier. A usage-metered plan and a flat-rate plan can post identical revenue while behaving completely differently once you account for cost. # Credits Benefit Source: https://docs.macropay.ai/features/benefits/credits Grant prepaid usage credits that top up a customer's meter balance automatically Prepaid credits are the cleanest way to fund metered usage up front — perfect for AI products where every token, request, or agent action draws down a balance. The Credits benefit grants a fixed number of units to a customer's [Usage Meter](/features/usage-based-billing/meters) balance, automatically, every time they buy or renew. No invoices to chase, no surprise overage bills. ## When credits are granted Attach the Credits benefit to any [product](/features/usage-based-billing/credits) and Macropay tops up the balance for you. The timing depends on the product type: | Product type | When units are granted | | ---------------- | ------------------------------------------------------- | | **Subscription** | At the start of every billing cycle — monthly or yearly | | **One-time** | Once, at the moment of purchase | For example, a "Pro" subscription that grants `50,000` inference credits per month gives the customer a fresh 50,000-unit balance on each renewal. A "Starter pack" one-time product that grants `10,000` credits hands them over a single time at checkout. Want a credits-only product with zero risk of overage? Skip the metered price entirely. With no price attached to the meter, billing never triggers — the customer simply spends down what you granted. See [Credits for usage-based billing](/features/usage-based-billing/credits). ## Rollover unused credits By default, leftover credits expire when the cycle ends. Enable **Rollover unused credits** to carry the unused balance forward instead: whatever a customer doesn't spend this cycle is added on top of next cycle's grant. Toggle the checkbox when you create or edit the benefit. Rollover changes apply only to credits issued **after** the change. Credits already on a customer's balance keep the rollover behavior they were granted with. ## Reading the balance in your app To gate features or show a usage widget, read the customer's current meter balance. The simplest path is [Customer State](/integrate/customer-state), which returns the full customer object including the balance of each active meter. For a single meter, query the [Customer Meters API](/api-reference/customer-meters/list) directly. Macropay tracks the balance but does not block usage when it hits zero — if there's a metered price, the overage is billed; if there isn't, spending simply continues. Enforce your own limits wherever you need a hard stop. ## Why prepaid credits fit AI billing Because Macropay is the [Merchant of Record](/features/usage-based-billing/introduction), every credit purchase is sold by us — we collect and remit sales tax and VAT worldwide, and your tax liability stays capped at one flat fee. That removes a real headache for usage-based AI products selling into dozens of jurisdictions. Credits also pair naturally with agent and AI workloads: fund a balance with one purchase, meter it down per token or per action, and grant a fresh allotment each cycle. Combined with [usage-based metering](/features/usage-based-billing/meters), it gives you prepaid AI billing without building a balance ledger yourself. # Custom Benefit Source: https://docs.macropay.ai/features/benefits/custom Deliver a private note or build your own entitlement logic for paying customers When none of the built-in benefits fit, the **Custom** benefit is your escape hatch. It does two jobs: it shows a private note that only paying customers can read, and it acts as a generic entitlement flag you can wire into your own integration logic. Because Macropay is the merchant of record, the purchase that unlocks this benefit is already tax-compliant and dispute-managed on your behalf — you just decide what the customer receives. ## What it's good for Reveal a secret link, a support address, or onboarding instructions only after payment clears. Use it as a named flag your backend reads to grant bespoke access that the standard benefits don't cover. ## Private note A custom benefit can carry a note that stays hidden until a customer has an active grant. Anything you'd only want a paying customer to see works well here: * A private scheduling link (for example, a [Cal.com](http://Cal.com) booking URL) * A dedicated support email or priority inbox * A coupon code, invite, or early-access instruction * A short welcome message specific to the product they bought The note renders for the customer in their [customer portal](/features/customer-portal) once the grant is active, and disappears if the entitlement lapses. ## Custom entitlements for integrations Each custom benefit you create is distinct, so you can attach several to different products and tell them apart in code. When a customer is granted (or loses) a benefit, Macropay emits webhook events you can act on. ```ts theme={null} // Listen for benefit grants and unlock your own feature flags. // See /api-reference/webhooks/benefit_grant.created for the payload shape. app.post("/webhooks/macropay", (req, res) => { const event = req.body; if (event.type === "benefit_grant.created") { const { customer_id, benefit_id } = event.data; // Match against the custom benefit you configured, then // grant whatever bespoke access this product implies. grantBespokeAccess(customer_id, benefit_id); } if (event.type === "benefit_grant.revoked") { const { customer_id, benefit_id } = event.data; revokeBespokeAccess(customer_id, benefit_id); } res.sendStatus(200); }); ``` Pair this with [usage-based billing](/features/usage-based-billing/introduction) when the bespoke access is metered — for example, an AI agent whose access tier and per-event cost are both governed by the same purchase. ## Steps In the dashboard, add a new benefit and choose **Custom**. Give it a clear internal name so you can recognize it in webhook payloads. Add the private message — a link, support address, or instructions — that should appear only after purchase. Add the benefit to any [product](/features/products) (one-time or subscription). Customers receive it automatically on a successful order. Subscribe to `benefit_grant.created` and `benefit_grant.revoked` webhooks to drive your own custom integration logic. Need a more specialized benefit instead? Macropay also ships [license keys](/features/benefits/license-keys), [file downloads](/features/benefits/file-downloads), [Discord access](/features/benefits/discord-access), [GitHub repository access](/features/benefits/github-access), and [prepaid credits](/features/benefits/credits). # Automate Discord Invites & Roles Source: https://docs.macropay.ai/features/benefits/discord-access Grant Discord invites and tier-based roles automatically when customers buy or subscribe Turn a purchase into a server invite. When a customer checks out, Macropay can drop them straight into your Discord with the right role attached — and pull access the moment a subscription lapses. No bots to babysit, no manual invite links, no spreadsheet of who paid for what. This is delivered as a **benefit**: an entitlement you attach to any product. Because Macropay is the merchant of record on every sale, the same flow also remits sales tax/VAT worldwide and keeps that liability off your books — you just decide who gets which role. ## What you can do * **Auto-invite** every buyer or subscriber into your server on payment * **Map tiers to roles** — `Supporter` for the $5 plan, `Pro` for the $25 plan, `Lifetime` for one-time buyers * **Connect more than one server** by creating multiple Discord benefits * **Auto-revoke** when a subscription ends, so access tracks billing state automatically ## Connect your Discord server Choose **Connect your Discord server**. You'll be sent to Discord to install the Macropay app on the server you want to manage. Discord will ask you to grant the scopes the app needs. All three are required — see the table below for what each one does. Back in Macropay, select which role this benefit hands out. That's the role a customer receives the instant they buy. ### Permissions explained | Permission | Why Macropay needs it | | ----------------- | -------------------------------------------------------------------------------------------------------------------- | | **Manage Roles** | Reads your server's roles so you can choose which one to assign, then applies it to paying customers. | | **Create Invite** | Generates the invite that adds a buyer to your server when they purchase the product or subscribe to a tier. | | **Kick Members** | Removes members tied to this benefit when their access should end — for example, a canceled or expired subscription. | The connected server is fixed once a benefit is created. Need to manage a second community? Create another Discord benefit and authorize the additional server. ## Attach the benefit to a product Open the product you want gated and scroll to the benefits section of the **Edit Product** form. Toggle on your Discord benefit and save. From then on, anyone who completes checkout for that product is invited and assigned the role you chose. Tier your community with one benefit per role. Create a `Pro` benefit and a `Founder` benefit, attach each to the matching subscription, and customers automatically land in the right place — upgrades and downgrades follow their billing state. ## How it fits the rest of Macropay Discord access is one of several entitlements you can bundle with a product. Mix and match it with other [benefits](/features/benefits/introduction) — [license keys](/features/benefits/license-keys), [file downloads](/features/benefits/file-downloads), or [GitHub repository access](/features/benefits/github-access) — on a single subscription. Membership, downloads, and source access all unlock from one checkout, and every order is tax-compliant out of the box because Macropay is the seller of record. # Automate Customer File Downloads Source: https://docs.macropay.ai/features/benefits/file-downloads Attach downloadable files to any product and let Macropay deliver secure, signed URLs to every buyer automatically Ship an ebook, a font pack, a desktop binary, or a 9GB model checkpoint, and Macropay turns it into a customer entitlement. Attach files to a product as a **File Downloads benefit** and every buyer or active subscriber gets a private, signed URL the moment their order clears — no storage to host, no download gating to build, no S3 to babysit. And because Macropay is the **merchant of record**, the sale that unlocks the file is already tax-handled: we calculate and remit sales tax / VAT worldwide and sit on the customer's statement as the seller, so you ship bytes instead of chasing compliance. ## What you get | Capability | Detail | | ------------------ | ----------------------------------------------------------------------------------- | | **File size** | Up to 10GB per file | | **File types** | Anything — ebooks, design assets, ZIPs, installers, full applications | | **Delivery** | Each customer receives a unique, signed download URL | | **Integrity** | SHA-256 checksum generated per file, exposable to customers for verification | | **Access control** | Grant or revoke per benefit; subscription cancellations remove access automatically | A File Downloads benefit can hold multiple files. Buyers of the product receive every enabled file in the benefit as a single bundle of downloads. ## Create a File Downloads benefit Go to **Benefits** in the Dashboard sidebar. Click **+ Add Benefit**. Choose **File Downloads** as the **Type**. Drag and drop files onto the dropzone (`Feed me some bytes`), or click it to open a file browser. Uploads stream straight into our storage as you add them. Attach the benefit to any product — one-time or subscription — and it's live. Every new order grants the files; cancelling a subscription revokes them. ## Manage the files in a benefit Click the filename to edit it inline. The new name is what customers see on their download. Drag and drop files into the order you want them presented to customers. Open the contextual menu (the dots) and choose **Copy SHA-256 Checksum**. Publish it alongside your release so customers can confirm the bytes they downloaded match what you shipped. Open the contextual menu and choose to disable the file. This is the safe way to retire a file without destroying it — see access rules below. Open the contextual menu and choose **Delete**. **Deleting a file is permanent — and it cuts off current customers too.** A delete removes the file from Macropay and our S3 storage entirely (only the metadata is retained). Active subscribers and past buyers lose access immediately. If you only want to stop *new* grants, disable the file instead. ## How access changes propagate Enabling, disabling, adding, and deleting files each behave differently for people who already own the benefit. Here's the full matrix: | Action | Existing customers | New customers | | ----------------------- | ----------------------- | ---------------- | | **Disable a file** | Keep access to it | Don't receive it | | **Enable / add a file** | Granted retroactively | Receive it | | **Delete a file** | Lose access immediately | Never receive it | **Disabling preserves legacy access on purpose.** Someone who bought before you disabled a file keeps their copy — useful for grandfathering older releases while you steer new buyers to the current version. **Re-enabling or adding files is retroactive.** The moment you add a new file or re-enable an old one, *every* current customer and subscriber with the benefit is granted access. Treat the benefit as the live source of truth for what buyers can download — shipping a v2 asset is as simple as uploading it. ## Pair downloads with usage or agent billing File downloads cover the "deliver the artifact" half of a product. For products where value accrues *after* delivery — an agent customers run, an API they call, a model they query — combine the download with [usage-based billing](/features/usage-based-billing/introduction) so the file is the on-ramp and metered usage is the revenue. A few patterns that work well: * **SDK or CLI + metered API** — ship the binary as a download, then meter calls against the [AI proxy](/features/llm-inference) or your own ingested events. * **Agent distribution** — hand over an agent bundle as a file, then bill its real work via the Signals API and attach [value receipts](/features/usage-based-billing/introduction) that certify the outcomes it produced. * **Templates + credits** — give away a starter file for free, then sell prepaid [credits](/features/benefits/credits) the customer burns down as they use the tooling. In every case Macropay stays the merchant of record across the bundle: one checkout, one tax-handled order, and the file plus any metered usage settle under the same flat fee. # Automate Private GitHub Repo(s) Access Source: https://docs.macropay.ai/features/benefits/github-access Grant and revoke private GitHub repo access automatically on every purchase, subscription, and cancellation. Ship code as a product. When a customer buys or subscribes, Macropay invites them as a collaborator on the private repositories you choose. When they cancel, access is pulled — no scripts, no spreadsheets, no manual invites to chase. Because Macropay is the merchant of record, the same flow also collects and remits sales tax/VAT on the sale and keeps that liability off your books, so you can focus on the repo and not the paperwork. ## What you can sell GitHub repository access is delivered as a **benefit** (entitlement) attached to any product — one-time or subscription: * **Subscriptions** → access granted on first payment, revoked the moment the subscription ends. * **One-time products** → buy once, keep collaborator access for life. * **Multiple repos** → attach several GitHub benefits to one product to bundle repositories from your organization(s). Common scenarios: | Use case | How it works | | ------------------------------ | ------------------------------------------------------------------ | | Sponsorware | Gate a repo behind a sponsorship tier until a funding goal is hit. | | Premium courses & starter kits | Sell the source alongside lessons, templates, and assets. | | Open-core / self-hosting | Charge for the private enterprise repo on top of your OSS core. | | Early access | Let paying users see feature branches before they land upstream. | | Sponsor-only discussions | Keep issues and Discussions private to paying collaborators. | Selling access to an AI agent or coding tool? Pair this benefit with [usage-based billing](/features/usage-based-billing/introduction) to charge for the repo *and* meter what the agent does once they're inside. ## Create the benefit In the dashboard sidebar, go to **Benefits** and click **+ New Benefit**. Set **Type** to **GitHub Repository Access**. Click **Connect your GitHub Account** and install the dedicated Macropay GitHub App on the repositories you want to automate. After authorizing, you're returned to the benefit form, now connected. Select the organization repository and the collaborator role to grant. Save, then attach the benefit to a product. **Why connect GitHub a second time and install a separate app?** Inviting collaborators requires permission to manage repository access — a sensitive scope. GitHub Apps can't request permissions progressively, so rather than ask *every* user for repo-management rights during normal GitHub login, this feature ships as a standalone app you install only on the repos you actually sell. ## Choose a repository Pick the organization repository you want to automate invites for. **Why organization repos only — not personal ones?** GitHub doesn't offer granular collaborator permissions on personal repositories; every collaborator effectively gets write access, meaning they could push commits and cut releases. To keep your code safe, personal repos are off by default. Need it anyway? Reach out and we can enable it for your account. ## Choose a role Select the access level granted to each collaborator: | Role | Recommendation | | -------- | ----------------------------------------------------------------- | | **Read** | **Default — use this.** Read-only access covers \~99.9% of cases. | | Triage | Advanced; manage issues/PRs without code access. | | Write | Discouraged — grants push access. | | Maintain | Discouraged — repo management without admin. | | Admin | Strongly discouraged — full control. | Anything above **Read** is discouraged unless you have a specific need and fully understand the impact. See the [GitHub role reference](https://docs.github.com/en/organizations/managing-user-access-to-your-organizations-repositories/managing-repository-roles/repository-roles-for-an-organization#permissions-for-each-role) for the exact permissions behind each role. Even with Read access, a collaborator can open a pull request ([details](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/creating-a-pull-request)). **Paid GitHub organizations are billed per seat.** GitHub counts each collaborator as a seat and charges your plan accordingly. Either run a free organization plan, or price your product high enough to cover the GitHub seat cost you'll incur for every buyer. # Automated Benefits Source: https://docs.macropay.ai/features/benefits/introduction Provision what a customer paid for the instant they pay — license keys, downloads, repo and Discord access, and prepaid credits — and revoke it the moment they stop. Collecting a payment is the easy half. The hard half is wiring up everything that has to happen *after* the charge clears — minting a license key, inviting the buyer to a private repo, dropping a Discord role, topping up a usage balance — and then unwinding all of it cleanly when a subscription lapses or a refund lands. **Benefits** are the entitlement engine that closes that gap. Define an entitlement once, attach it to any product or subscription tier, and Macropay grants access at purchase and revokes it at expiry — automatically. There are no fulfillment cron jobs to babysit and no `customer.subscription.deleted` webhooks to reconcile by hand. And because Macropay is your [Merchant of Record](/merchant-of-record/introduction), one system does the whole job: it charges the card, remits sales tax and VAT on your behalf, limits your tax liability, *and* drives fulfillment. Billing and entitlement never drift apart, because they're the same flow. ## The benefit types you can attach A benefit is a reusable resource — configure it once, then link it from as many products as you like. Top up a customer's usage-meter balance. The backbone of prepaid, metered, and AI/agent billing. Mint branded keys with your own format, expiry, and activation/validation rules. Serve downloadable assets of any type — up to 10 GB per file. Auto-invite paying customers to one or more private repositories. Issue invites and assign server roles to customers and active subscribers. Need something off this list? Reach for a **custom benefit**, and watch this space — more built-in types are landing over time. Building an AI product, API, or autonomous agent? Pair a **Prepaid Credits** benefit with a [usage meter](/features/usage-based-billing/introduction) so each plan grants a balance that draws down as the customer consumes tokens, calls, or agent runs. That pairing is the foundation for [metered AI and agent-outcome billing](/features/usage-based-billing/introduction). ## Define once, reuse everywhere The thing that makes benefits manageable at scale is that each one is a **standalone resource** — not a blob of config copied into every product. You attach the same benefit to one product or to fifty. | Benefit as a shared resource | Entitlement copied per product | | ----------------------------------------- | ------------------------------------- | | Define once, attach to many products | Re-enter the same config every time | | Edit in one place — it updates everywhere | Track down and patch every copy | | One source of truth, no drift | Duplicate data that silently diverges | | Clean to manage, clean for customers | Cluttered and error-prone | **Concrete example.** You sell a developer tool through three SKUs: `Indie Monthly`, `Team Annual`, and a `Lifetime` license. A single "Insiders Discord role" benefit attaches to all three. Rename the role or swap the server, and every plan picks up the change at once — no per-product edits. ## Who has access at any moment Macropay derives entitlements from the customer's *current* standing, so the access list is always correct without sweeping for stale grants. | Customer state | Access? | | ------------------------------------------------ | ------------------------------------ | | Active subscriber on a tier carrying the benefit | Yes | | Bought a one-time product carrying the benefit | Yes — for life | | Subscription cancelled, expired, or refunded | No | | Never purchased | No | One-time purchases grant **lifetime** access to their benefits. Subscription benefits live and die with the subscription — when it lapses, the associated access (repo invite, Discord role, license key) is pulled automatically. ## Two ways to set one up Add a benefit directly in the product form as you create or edit an offering — handy when you're standing up a new plan and want everything in one pass. Manage every benefit in one place under **Benefits**, then attach existing ones to any product or tier. Best when a single entitlement spans several plans. Once a benefit is attached, fulfillment runs itself for every future purchase, renewal, and cancellation. You stop shipping access by hand — Macropay grants and revokes it for you. # Automate Customer License Key Management Source: https://docs.macropay.ai/features/benefits/license-keys Issue, validate, and revoke software license keys automatically — no key server to build, no sales tax to remit. Shipping a desktop app, a CLI, a plugin, or a paid API? You need a way to prove a customer is entitled to use it. Macropay turns that into a checkbox: attach a **License Keys** benefit to any product, and every buyer is automatically issued a unique key the moment they pay — backed by a validation endpoint you call at runtime. Because Macropay is your **merchant of record**, the same flow also handles global sales tax and VAT, absorbs PCI scope, and lists Macropay as the seller on the customer's statement. You sell access; we handle the financial and compliance plumbing behind it. ## What you get out of the box | Capability | What it does | | --------------------- | --------------------------------------------------------------------------------- | | **Branded prefixes** | Ship keys like `ACME_` so they're recognizable in support tickets and logs | | **Auto-expiration** | Keys can lapse `N` days, months, or years after purchase | | **Activation limits** | Cap how many devices, seats, or IPs a key can run on | | **Custom conditions** | Pin a key to a major version, MAC address, or any value you choose | | **Usage quotas** | Meter consumption per key and increment it on each validation | | **Auto-revocation** | A cancelled subscription instantly invalidates its key | No key server to host, no expiry cron to babysit, no admin panel to build for self-service. ## Create the benefit Select **Benefits** in the dashboard sidebar. Click **+ New Benefit**. Set the **Type** to **License Keys**, then configure the options below. ### Branded prefixes Give keys a recognizable identity. A prefix of `ACME` produces keys such as `ACME_` — useful when a customer pastes one into a ticket and you want to spot it at a glance. ### Automatic expiration Want access to lapse a fixed period after purchase? Set an expiry window and Macropay stamps `expires_at` on every issued key. Perpetual-with-updates, annual, or short trial licenses all map cleanly onto this. ### Activation limits Require a key to be **activated** before it validates. This caps usage to a set number of instances — devices, IPs, CI runners, whatever you define — and lets customers self-manage their slots from the customer portal. No bespoke "manage your devices" screen to build on your side. ### Usage quotas Reselling LLM tokens, render minutes, or API calls? Attach a usage quota to the key and increment it on each validation. The key carries both `usage` and `limit_usage`, so a single call tells you whether the customer still has headroom. For consumption that varies in real time — per-token AI spend, metered API traffic — pair license keys with Macropay [usage-based billing](/features/usage-based-billing/introduction). Ingest events through meters, bill on what was actually consumed, and let the license key gate access while the meter handles the math. This is also how teams bill **AI agents** by usage, activity, or outcome. ## What the customer sees The instant a purchase or subscription clears, the buyer is issued a unique key, visible on their purchases page in the customer portal. From there they can: * View and copy the key * Check the expiration date, if one is set * See remaining usage, if a quota applies * Deactivate activations, if activation limits are enabled It's a self-serve surface you get for free — no portal to design, host, or maintain. ## Integrate the API Wiring license keys into your app, library, or API takes two endpoints: an optional **activate** call and a **validate** call you run each session. ### Step 1 — Activate (only with activation limits) If a benefit caps activation instances, register an activation before the key can validate. Each activation represents one device, seat, or environment counting against the limit. No activation limit configured? Skip straight to validation. ```bash Terminal theme={null} curl -X POST https://api.macropay.ai/v1/customer-portal/license-keys/activate \ -H "Content-Type: application/json" \ -d '{ "key": "1C285B2D-6CE6-4BC7-B8BE-ADB6A7E304DA", "organization_id": "fda84e25-7b55-4d67-916d-60ead04ff61f", "label": "macbook-pro-ci", "conditions": { "major_version": 4 }, "meta": { "ip": "84.19.145.194" } }' ``` The customer's license key, captured from input in your app. Your organization ID, found in your dashboard settings. A human-readable label for this activation, e.g. the machine or environment name. Custom values to re-check on future validations — IP, MAC address, major version, and so on. Arbitrary metadata to store alongside the activation. #### Response (200 OK) ```json theme={null} { "id": "b6724bc8-7ad9-4ca0-b143-7c896fcbb6fe", "license_key_id": "508176f7-065a-4b5d-b524-4e9c8a11ed63", "label": "macbook-pro-ci", "meta": { "ip": "84.19.145.194" }, "created_at": "2024-09-02T13:48:13.251621Z", "modified_at": null, "license_key": { "id": "508176f7-065a-4b5d-b524-4e9c8a11ed63", "organization_id": "fda84e25-7b55-4d67-916d-60ead04ff61f", "user_id": "d910050c-be66-4ca0-b4cc-34fde514f227", "benefit_id": "32a8eda4-56cf-4a94-8228-792d324a519e", "key": "1C285B2D-6CE6-4BC7-B8BE-ADB6A7E304DA", "display_key": "****-E304DA", "status": "granted", "limit_activations": 3, "usage": 0, "limit_usage": 100, "validations": 0, "last_validated_at": null, "expires_at": "2026-08-30T08:40:34.769148Z" } } ``` Hold onto the returned activation `id` — you'll pass it as `activation_id` when you validate. ### Step 2 — Validate Validate the key on each session of your app, library, or API through the [validate endpoint](/api-reference/customer-portal/license-keys/validate). This is also where you increment usage when a quota applies. ```bash Terminal theme={null} curl -X POST https://api.macropay.ai/v1/customer-portal/license-keys/validate \ -H "Content-Type: application/json" \ -d '{ "key": "1C285B2D-6CE6-4BC7-B8BE-ADB6A7E304DA", "organization_id": "fda84e25-7b55-4d67-916d-60ead04ff61f", "activation_id": "b6724bc8-7ad9-4ca0-b143-7c896fcbb6fe", "conditions": { "major_version": 4 }, "increment_usage": 15 }' ``` The customer's license key, captured from input in your app. Your organization ID, found in your dashboard settings. The activation to validate against. Required when activation limits are enabled and in use (see Step 1). When validating an activation, pass the same conditions object you registered it with. Amount to add to the key's usage counter on this validation. #### Response (200 OK) ```json theme={null} { "id": "508176f7-065a-4b5d-b524-4e9c8a11ed63", "organization_id": "fda84e25-7b55-4d67-916d-60ead04ff61f", "user_id": "d910050c-be66-4ca0-b4cc-34fde514f227", "benefit_id": "32a8eda4-56cf-4a94-8228-792d324a519e", "key": "1C285B2D-6CE6-4BC7-B8BE-ADB6A7E304DA", "display_key": "****-E304DA", "status": "granted", "limit_activations": 3, "usage": 15, "limit_usage": 100, "validations": 5, "last_validated_at": "2024-09-02T13:57:00.977363Z", "expires_at": "2026-08-30T08:40:34.769148Z", "activation": { "id": "b6724bc8-7ad9-4ca0-b143-7c896fcbb6fe", "license_key_id": "508176f7-065a-4b5d-b524-4e9c8a11ed63", "label": "macbook-pro-ci", "meta": { "ip": "84.19.145.194" }, "created_at": "2024-09-02T13:48:13.251621Z", "modified_at": null } } ``` A `granted` status means the key is live; check `expires_at` and the `usage` / `limit_usage` pair to decide whether to unlock your feature. **Always send `organization_id`.** It scopes validation to your organization so a key issued by one Macropay organization can never be accepted by another. If you ship more than one license-key product, also check the `benefit_id` on the response to confirm the key belongs to the specific product you're gating — Macropay won't disambiguate that for you. # Basis Theory checkout Source: https://docs.macropay.ai/features/checkout/basis-theory Card data is tokenized in a PCI DSS Level 1 vault and never touches your servers or Macropay's When a customer types their card number into Macropay checkout, those digits go straight into a sandboxed, cross-origin iframe and are tokenized inside a **PCI DSS Level 1 compliant** vault. The card number, expiry, and CVC never reach your origin, your backend, or Macropay's. What you and the acquirer see is a non-sensitive token — never the raw PAN. This is the same capture layer whether you ship a hosted [Checkout Link](/features/checkout/links), drop in the [embedded checkout](/features/checkout/embed), or build a custom UI on [Checkout Sessions](/features/checkout/session). One iframe, one PCI posture, three integration styles. ## Why tokenization in a vault matters The card form lives in iframes served from a vault domain, not from your page. Every keystroke is intercepted there, exchanged for a token, and only the token crosses back to Macropay to authorize the charge. The practical payoff: | Benefit | What it means for you | | ---------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Smallest PCI scope** | Sensitive card data never lands on your infrastructure, so your business qualifies for the lightest self-assessment tier. | | **Nothing to leak** | No PAN in your logs, error tracker, analytics, or database — there's no card data on your side to spill in the first place. | | **Merchant of Record** | Macropay charges the card as the seller of record on its own acquirer relationships. You don't bring a processor, pass your own PCI audit, or remit sales tax/VAT — we do, and we cap your tax liability. | Because Macropay is the Merchant of Record, the PCI burden, tax remittance, and chargeback handling all sit with us. Tokenization is the mechanism; MoR is the coverage. ## What gets captured The form renders three independent fields, each in its own sandboxed iframe: 1. **Card number** — with live brand detection. A row of brand icons on the right edge lights up the detected network as the customer types. 2. **Expiry** (MM/YY) 3. **CVC** Splitting the form into separate iframes keeps each field individually focusable, tabbable, and validatable, while every field stays cross-origin from your page. Tokenization runs as a single vault call on submit; Macropay receives the resulting token and nothing else. ## Embedding the form How much you do depends on which surface you choose: Using a [Checkout Link](/features/checkout/links) or the [embedded checkout](/features/checkout/embed)? The vault iframes are already inside Macropay's hosted checkout. Nothing to wire up. Building on [Checkout Sessions](/features/checkout/session)? Mount the `` React component from `@macropayments/checkout` and the card form appears, fully orchestrated. For a custom UI, the component handles iframe orchestration, inline validation, and tokenization for you: ```tsx theme={null} import { CheckoutForm } from '@macropayments/checkout/react' router.push(`/receipt/${order.id}`)} /> ``` You wire up `onConfirmed` to route the buyer after the charge clears. You never receive — or have to protect — the raw card data. ## Theming the fields Vault iframes don't accept style updates after they mount, so a theme switch re-mounts them in the new color scheme. Macropay watches the `` toggle through a debounced `MutationObserver` and does the re-mount for you. Macropay's hosted checkout (links + embed) renders **light-mode only** by design. The card fields stay on a crisp light surface for every merchant, even if you set `data-macropay-checkout-theme="dark"` on a link — the rest of the page goes dark, the card form stays light. This keeps brand-icon contrast and field affordances correct across every device and OS theme. Building your own UI on Checkout Sessions? You can opt the card fields into a dark theme — just know you own the contrast for the brand-icon row and the field error states. Inside the form, the visual states are tuned to sit pixel-aligned next to your own inputs: * **Interaction states** — focus, hover, error, and disabled are driven by stateful classes on the wrapper, not `:focus` selectors. The iframe takes focus, so the wrapper reflects it. * **Borders and rings** — border color, ring width, and transitions match Macropay's `Input` atom, so the card fields line up beside the Cardholder Name input. * **Brand row** — non-matching brands fade to 0.25 opacity while the detected brand pops to full opacity. ## Supported card brands The on-screen brand row shows live indicators for the four most common networks: **Visa, Mastercard, American Express, and Discover.** Tokenization also covers Diners Club, JCB, UnionPay, Maestro, Hipercard, and Elo — buyers can pay with any of those even though the icon row stays focused on the major four. Which cards actually clear is governed by Macropay's acquirer and the buyer's billing country, not by the iframe. ## Wallets When the browser supports them, **Apple Pay** and **Google Pay** appear **above** the card form. The wallet sheet skips the card iframes entirely: Apple or Google hands back a tokenized payment method that Macropay relays straight to the acquirer. Wallets keep working even if the card vault is unreachable, because they route through the browser rather than the iframe. For embedded checkout, wallets need a one-time domain validation — see [enabling wallet payment methods](/features/checkout/embed#enabling-wallet-payment-methods-apple-pay-google-pay-etc). ## FAQ The visible chrome — border, padding, radius, shadow — is yours to re-skin with Tailwind classes on the wrapper. The text *inside* the iframe (font family, size, color) is limited to a small set of style tokens the vault exposes, which Macropay tunes to match the wrapper. Open a support request if you need a non-default token. Split fields let each input be tabbable, focusable, and validated on its own while keeping every keystroke cross-origin from your page. One field, one iframe, one isolation boundary. Yes. Each iframe is labelled and exposes its validation state to assistive tech. Errors render below a field only after blur, and only when the field is non-empty and invalid — no shouting while the customer is still typing. The customer sees a "card capture is unavailable" message and the submit button stays disabled. Wallets (Apple Pay / Google Pay) still work because they go through the browser. Macropay monitors vault uptime and posts any incidents to its status page. # Embedded Checkout Source: https://docs.macropay.ai/features/checkout/embed Sell from inside your own site — no redirect, no PCI scope, tax handled for you Keep buyers on your page through the entire purchase. The embedded checkout opens our payment form in an overlay on top of your site, so customers never bounce to a hosted page mid-funnel. Card data is captured and tokenized in our PCI DSS Level 1 compliant vault, and because Macropay is the Merchant of Record, sales tax and VAT are calculated, collected, and remitted on the order — you ship the integration, we own the tax and compliance. There are two ways in: * **Drop-in snippet** — paste two tags into any HTML page or CMS. Zero build step. * **JavaScript library** — install the package for SPAs, event hooks, and programmatic control. ## Drop-in snippet Works anywhere you can paste HTML — a landing page, a Framer site, a Webflow block, a docs page. Start from a [checkout link](/features/checkout/links). Open the link in your dashboard and click **Copy Embed Code** to grab a ready-to-paste snippet, which looks like this: ```typescript theme={null} Buy the Pro plan ``` The link renders inline and opens the checkout overlay on click. Style the trigger however you like — any element works as long as it carries the `data-macropay-checkout` attribute. Use `data-macropay-checkout-theme="dark"` to match a dark UI. ## JavaScript library For a React, Vue, or other bundled app, injecting a raw `