Payment API
Use Revenue → Connectors → Use another billing system to create a named production billing source. Alternatively, call POST /api/v1/apps/{app}/revenue/payment-sources with {"label":"Billing service","currency":"USD"}. Source creation in the console requires a fresh passkey.
Send payment state only after your server verifies it with your billing provider. This API records your report; it does not charge customers, verify settlement independently, or grant entitlements. Reported payment API labels distinguish these records in Payments. Cash totals and customer lifetime revenue include them; provider-attribution reports exclude them. Subscription MRR is not inferred.
Use a server-side Management API token with write scope and revenue.write. Public and secret ingest keys cannot write billing state. Keep management tokens out of browsers and mobile applications. Sources bind the production environment; env=staging cannot write to them. Revenue reports remain application-wide.
POST /api/v1/apps/{app}/revenue/payment-sources/{sourceId}/payments
Authorization: Bearer YOUR_MANAGEMENT_TOKEN
Content-Type: application/json
{
"version": 1,
"eventId": "invoice_123_paid",
"paymentId": "invoice_123",
"observedAt": "2026-09-07T00:00:00Z",
"paidAt": "2026-09-07T00:00:00Z",
"amountCents": 4900,
"currency": "USD",
"status": "succeeded",
"refundedCents": 0,
"customer": {"id": "customer_123", "email": "customer@example.com"}
}
Use your real provider IDs and timestamps. Amounts are hundredths of the named currency, matching the existing revenue ledger: 4900 USD means USD 49; JPY 100 is 10000 JPY. Convert native provider minor units before submission when its currency exponent differs from two. Values must be safe nonnegative integers, at most one trillion; refunds cannot exceed the charge. No exchange conversion occurs at ingestion.
| Situation | Send / result |
|---|---|
| Retry | Same eventId and identical payload → duplicate: true |
| Changed reuse | Same eventId with different fields → HTTP 409 |
| Refund | Same paymentId, new eventId, later observedAt, status: "refunded", cumulative refundedCents |
| Older snapshot | Acknowledged with applied: false; current payment remains intact |
| Equal-time conflict | Different status/refund at the same observedAt → HTTP 409 |
| Another charge | New paymentId; original amount, currency, linked customer and settlement date cannot be reassigned |
| Disabled source / wrong environment | HTTP 404 |
pending and failed require paidAt: null; settled states require it. Dates start in 2000, paidAt cannot exceed observedAt, and observedAt may be at most five minutes ahead. A settled payment cannot become pending/failed, and cumulative refunds cannot decrease. Customers join existing People records by normalized email; console emails retain the existing PII permission gate.
Receipts and their idempotency hashes last for the source's lifetime. Reports are limited to 16 KB and existing per-token rate limits; an application may have at most 25 payment API sources. Writes, receipts, recompute jobs and the audit event commit together. Deleting a source removes its normalized billing records, as explained by the connector deletion control. Use the connector's recent events and the Payments table to verify delivery, then compare totals with your billing system.