Payments
Standalone payments and the payment log, including review resolution.
Concepts, behaviour and examples: Payments guide.
GET /v1/payments
List payments
Cursor-paginated (limit, starting_after).
- Authentication
- Merchant API key
- Effect
- Read-only.
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
limit | integer | no | Page size (1–100). · ≥ 1, ≤ 100, default 20 |
starting_after | string | no | Object ID; return objects after it (ascending ID order). · 26 chars |
subscription | string | no | Filter by subscription ID. · 26 chars |
customer | string | no | Filter by customer ID. · 26 chars |
status | "pending" | "succeeded" | "failed" | "refunded" | no | Filter by status. |
type | "standalone" | "subscription" | no | Standalone vs subscription charges. |
review_status | "none" | "open" | "resolved" | no | Filter by review state; open lists payments needing a disposition. |
Responses
| Status | Meaning | Body / error codes |
|---|---|---|
| 200 | Success. | object |
| 400 | Invalid request. | invalid_query_param |
| 401 | Missing or invalid API key. | authentication_error |
| 429 | Rate limited; honor retry-after. | rate_limit_exceeded too_many_auth_failures |
| 500 | Server error. | api_error |
Example response
{
"object": "list",
"url": "string",
"has_more": false,
"data": [
{
"object": "payment",
"id": "01JB9XK3M2R7TQ8N4V6Y0ZPA1C",
"merchant_id": "01JB9XK3M2R7TQ8N4V6Y0ZPA1C",
"subscription_id": "01JB9XK3M2R7TQ8N4V6Y0ZPA1C",
"customer_id": "01JB9XK3M2R7TQ8N4V6Y0ZPA1C",
"instrument_id": "01JB9XK3M2R7TQ8N4V6Y0ZPA1C",
"amount": 1,
"currency": "RON",
"status": "pending",
"netopia_ntp_id": "string",
"netopia_status_code": 1,
"error_message": "string",
"period_start": "2026-10-25T18:00:00.000Z",
"period_end": "2026-10-25T18:00:00.000Z",
"description": "string",
"metadata": {},
"proration": false,
"review": {
"status": "open",
"reason": "subscription_expired",
"opened_at": "2026-10-25T18:00:00.000Z",
"resolution": "refunded_externally",
"note": "string",
"resolved_at": "2026-10-25T18:00:00.000Z",
"resolved_by_api_key_id": "01JB9XK3M2R7TQ8N4V6Y0ZPA1C"
},
"created_at": "2026-10-25T18:00:00.000Z",
"payment_link_id": "01JB9XK3M2R7TQ8N4V6Y0ZPA1C",
"payer_email": "string"
}
]
}POST /v1/payments
Create a standalone payment
Charges a customer's stored instrument once, with no subscription (setup fee, top-up…). The instrument must have completed hosted enrollment. A 201 does not mean the customer has paid — read status, poll GET /v1/payments/{id} or consume payment.succeeded/payment.failed. Without a key the charge is submitted before the response; with one it is committed and submitted right after, so the first response reports pending. A decline is final (no retries; retry_at: null); timeouts/5xx stay pending until the NETOPIA Payments callback or reconciliation settles them. No refunds exist.
- Authentication
- Merchant API key
- Effect
- Creates a payment and charges the instrument; emits
payment.succeededorpayment.failed.
Headers
| Name | Type | Required | Description |
|---|---|---|---|
Idempotency-Key | string | no | Makes retries safe: same key + body replays the stored response (idempotency-replayed: true); a different body returns 409 idempotency_key_in_use. · ≤ 96 chars |
Request body
| Field | Type | Required | Description |
|---|---|---|---|
customer_id | string | yes | ULID · 26 chars |
instrument_id | string | no | Defaults to the customer's default instrument; must belong to the customer and not be detached. · 26 chars |
amount | integer | yes | Minor units. · > 0, ≤ 2147483647 |
currency | "RON" | "EUR" | "USD" | yes | Currency. Case-insensitive on input, stored uppercase. |
description | string | no | Also sent to NETOPIA Payments as the order description. · ≤ 500 chars |
metadata | object | no | Free-form key/value data. Each value ≤16 KiB of JSON, ≤8 nesting levels, ≤100 properties/elements. |
Example request
{
"customer_id": "01JB9XK3M2R7TQ8N4V6Y0ZPA1C",
"instrument_id": "01JB9XK3M2R7TQ8N4V6Y0ZPA1C",
"amount": 1,
"currency": "RON",
"description": "string",
"metadata": {}
}Responses
| Status | Meaning | Body / error codes |
|---|---|---|
| 201 | Created; check status. | Payment |
| 400 | Invalid body, no usable instrument, or instrument not verified. | validation_failed instrument_required instrument_enrollment_required |
| 401 | Missing or invalid API key. | authentication_error |
| 404 | Customer, or instrument not found/detached/another customer's. | resource_missing |
| 409 | State conflict. | idempotency_key_in_use |
| 429 | Rate limited; honor retry-after. | rate_limit_exceeded too_many_auth_failures |
| 500 | Server error. | api_error |
Example response
{
"object": "payment",
"id": "01JB9XK3M2R7TQ8N4V6Y0ZPA1C",
"merchant_id": "01JB9XK3M2R7TQ8N4V6Y0ZPA1C",
"subscription_id": "01JB9XK3M2R7TQ8N4V6Y0ZPA1C",
"customer_id": "01JB9XK3M2R7TQ8N4V6Y0ZPA1C",
"instrument_id": "01JB9XK3M2R7TQ8N4V6Y0ZPA1C",
"amount": 1,
"currency": "RON",
"status": "pending",
"netopia_ntp_id": "string",
"netopia_status_code": 1,
"error_message": "string",
"period_start": "2026-10-25T18:00:00.000Z",
"period_end": "2026-10-25T18:00:00.000Z",
"description": "string",
"metadata": {},
"proration": false,
"review": {
"status": "open",
"reason": "subscription_expired",
"opened_at": "2026-10-25T18:00:00.000Z",
"resolution": "refunded_externally",
"note": "string",
"resolved_at": "2026-10-25T18:00:00.000Z",
"resolved_by_api_key_id": "01JB9XK3M2R7TQ8N4V6Y0ZPA1C"
},
"created_at": "2026-10-25T18:00:00.000Z",
"payment_link_id": "01JB9XK3M2R7TQ8N4V6Y0ZPA1C",
"payer_email": "string"
}GET /v1/payments/:id
Retrieve a payment
- Authentication
- Merchant API key
- Effect
- Read-only.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
id | string | yes | Object ID (ULID). · 26 chars |
Responses
| Status | Meaning | Body / error codes |
|---|---|---|
| 200 | Success. | Payment |
| 401 | Missing or invalid API key. | authentication_error |
| 404 | Not found, or not yours. | resource_missing |
| 429 | Rate limited; honor retry-after. | rate_limit_exceeded too_many_auth_failures |
| 500 | Server error. | api_error |
Example response
{
"object": "payment",
"id": "01JB9XK3M2R7TQ8N4V6Y0ZPA1C",
"merchant_id": "01JB9XK3M2R7TQ8N4V6Y0ZPA1C",
"subscription_id": "01JB9XK3M2R7TQ8N4V6Y0ZPA1C",
"customer_id": "01JB9XK3M2R7TQ8N4V6Y0ZPA1C",
"instrument_id": "01JB9XK3M2R7TQ8N4V6Y0ZPA1C",
"amount": 1,
"currency": "RON",
"status": "pending",
"netopia_ntp_id": "string",
"netopia_status_code": 1,
"error_message": "string",
"period_start": "2026-10-25T18:00:00.000Z",
"period_end": "2026-10-25T18:00:00.000Z",
"description": "string",
"metadata": {},
"proration": false,
"review": {
"status": "open",
"reason": "subscription_expired",
"opened_at": "2026-10-25T18:00:00.000Z",
"resolution": "refunded_externally",
"note": "string",
"resolved_at": "2026-10-25T18:00:00.000Z",
"resolved_by_api_key_id": "01JB9XK3M2R7TQ8N4V6Y0ZPA1C"
},
"created_at": "2026-10-25T18:00:00.000Z",
"payment_link_id": "01JB9XK3M2R7TQ8N4V6Y0ZPA1C",
"payer_email": "string"
}POST /v1/payments/:id/resolve
Resolve a payment review
Records the merchant's attestation that a late successful charge (on an expired/canceled subscription) was refunded externally or service provided. No refund is submitted or verified; payment status stays succeeded. An identical unkeyed resolution returns the existing result.
- Authentication
- Merchant API key
- Effect
- Marks the review resolved with the calling key's ID; emits
payment.review_resolved.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
id | string | yes | Object ID (ULID). · 26 chars |
Headers
| Name | Type | Required | Description |
|---|---|---|---|
Idempotency-Key | string | no | Makes retries safe: same key + body replays the stored response (idempotency-replayed: true); a different body returns 409 idempotency_key_in_use. · ≤ 96 chars |
Request body
| Field | Type | Required | Description |
|---|---|---|---|
resolution | "refunded_externally" | "service_provided" | yes | |
note | string | yes | Identify the external action/reference. · ≥ 1 chars, ≤ 1000 chars |
Example request
{
"resolution": "refunded_externally",
"note": "string"
}Responses
| Status | Meaning | Body / error codes |
|---|---|---|
| 200 | Success. | Payment |
| 400 | Invalid body. | validation_failed invalid_json |
| 401 | Missing or invalid API key. | authentication_error |
| 404 | Not found, or not yours. | resource_missing |
| 409 | Review is already resolved differently, or not open. | payment_review_already_resolved payment_review_not_open |
| 429 | Rate limited; honor retry-after. | rate_limit_exceeded too_many_auth_failures |
| 500 | Server error. | api_error |
Example response
{
"object": "payment",
"id": "01JB9XK3M2R7TQ8N4V6Y0ZPA1C",
"merchant_id": "01JB9XK3M2R7TQ8N4V6Y0ZPA1C",
"subscription_id": "01JB9XK3M2R7TQ8N4V6Y0ZPA1C",
"customer_id": "01JB9XK3M2R7TQ8N4V6Y0ZPA1C",
"instrument_id": "01JB9XK3M2R7TQ8N4V6Y0ZPA1C",
"amount": 1,
"currency": "RON",
"status": "pending",
"netopia_ntp_id": "string",
"netopia_status_code": 1,
"error_message": "string",
"period_start": "2026-10-25T18:00:00.000Z",
"period_end": "2026-10-25T18:00:00.000Z",
"description": "string",
"metadata": {},
"proration": false,
"review": {
"status": "open",
"reason": "subscription_expired",
"opened_at": "2026-10-25T18:00:00.000Z",
"resolution": "refunded_externally",
"note": "string",
"resolved_at": "2026-10-25T18:00:00.000Z",
"resolved_by_api_key_id": "01JB9XK3M2R7TQ8N4V6Y0ZPA1C"
},
"created_at": "2026-10-25T18:00:00.000Z",
"payment_link_id": "01JB9XK3M2R7TQ8N4V6Y0ZPA1C",
"payer_email": "string"
}