DocumentationPOST Create a payment
Payments API
POST
/paymentsCreate a payment
Starts a payment with the provider connected in this environment and returns a checkout URL. The amount is an integer in the smallest currency unit.
Endpoint
POST https://api.veyqo.org/api/v1/payments
Server secret key required. Scope: payments:write. The key determines the project and environment; no project-selection header is accepted.
For each new operation, generate and persist one OPERATION_IDEMPOTENCY_KEY. Retry the unchanged request with that same key. Do not generate a new key on every retry.
Example request
Examples use illustrative IDs and provider URLs. Replace path variables with IDs returned by your own requests. Never paste a live secret into client code or a public tool.
curl --request POST "https://api.veyqo.org/api/v1/payments" \
--header "Authorization: Bearer $VEYQO_SECRET_KEY" \
--header "Accept: application/json" \
--header "Idempotency-Key: $OPERATION_IDEMPOTENCY_KEY" \
--header "Content-Type: application/json" \
--data '{
"amount": 50000,
"currency": "NGN",
"reference": "ORDER-1001",
"provider": "paystack",
"customer": {
"email": "customer@example.com",
"name": "Ada Lovelace"
},
"metadata": {
"order_id": "1001"
}
}'Request body
| Field | Type | Presence | Description |
|---|---|---|---|
| amount | integer | Required | Integer minor-unit amount. NGN 500.00 is 50000; JPY 500 is 500. Minimum 1. |
| currency | string | Required | Three-letter currency supported by the selected provider. Maximum 3 characters. |
| reference | string | null | Optional | Unique within this environment. Omitted, defaults to the new payment ID. Maximum 190 characters. |
| description | string | null | Optional | Payment description. Maximum 255 characters. |
| callback_url | string | null | Optional | HTTPS return URL. Verify payment server-side after the redirect. Maximum 2048 characters. Format: uri. |
| provider | string | null | Optional | Provider slug. Omit only when the environment has a default or exactly one active provider. Maximum 40 characters. |
| customer | object | Optional | See the JSON example. |
| customer.email | string | Required | Required for nested customer requests. Maximum 190 characters. Format: email. |
| customer.name | string | null | Optional | Maximum 190 characters. |
| customer.phone | string | null | Optional | Maximum 32 characters. |
| customer_email | string | Optional | Legacy flat alternative to customer.email. Maximum 190 characters. Format: email. |
| customer_name | string | null | Optional | Legacy flat name. Maximum 190 characters. |
| customer_phone | string | null | Optional | Legacy flat phone. Maximum 32 characters. |
| metadata | object | array | null | Optional | Your non-sensitive JSON metadata. Objects are recommended; do not store secrets or card details. |
Supply customer.email in a nested customer object, or use the legacy customer_email field. When a customer object is present, its email is required. The API validates the form you actually sent.
Successful response · 201
HTTP success means the request was accepted. A pending payment or refund is not completed merely because the response is 200 or 201.
{
"success": true,
"data": {
"id": "pay_01M6EB66SC61Q3RQYAN0693DGM",
"object": "payment",
"reference": "ORDER-1001",
"status": "pending",
"amount": 50000,
"amount_refunded": 0,
"currency": "NGN",
"customer": {
"email": "customer@example.com",
"name": "Ada Lovelace",
"phone": null
},
"description": "Order 1001",
"checkout_url": "https://checkout.paystack.com/example",
"provider": "paystack",
"provider_reference": "provider_reference_example",
"metadata": {
"order_id": "1001"
},
"failure_code": null,
"failure_message": null,
"authorized_at": null,
"succeeded_at": null,
"failed_at": null,
"created_at": "2026-11-02T12:00:00+00:00"
}
}Response fields
| Field in data | Type | Presence | Description |
|---|---|---|---|
| id | string | Required | Globally unique Veyqo payment ID; use it for reconciliation. |
| object | "payment" | Required | See the JSON example. |
| reference | string | Required | Merchant reference unique within the project environment; defaults to the payment ID. |
| status | string | Required | Normalized payment state. Do not fulfil pending, processing, failed or cancelled payments. Values: created, pending, processing, succeeded, failed, cancelled, refunded, partially_refunded. |
| amount | integer | Required | Original amount in the smallest currency unit. |
| amount_refunded | integer | Required | Successfully refunded amount in the smallest currency unit. |
| currency | string | Required | Three-letter currency code. |
| customer | object | Required | See the JSON example. |
| customer.email | string | Required | Customer email address. |
| customer.name | string | null | Required | Customer name. |
| customer.phone | string | null | Required | Customer phone. |
| description | string | null | Required | Optional description. |
| checkout_url | string | null | Required | Provider checkout URL for direct payments; may be null depending on the flow. Format: uri. |
| provider | string | Required | Selected provider slug. |
| provider_reference | string | null | Required | Reference the provider assigned, not your order reference. |
| metadata | object | array | Required | Your non-sensitive JSON metadata. Objects are recommended; do not store secrets or card details. |
| failure_code | string | null | Required | Machine-readable payment failure reason, separate from API error codes. |
| failure_message | string | null | Required | Human-readable payment failure reason. |
| authorized_at | string | null | Required | ISO 8601 timestamp. Format: date-time. |
| succeeded_at | string | null | Required | ISO 8601 timestamp. Format: date-time. |
| failed_at | string | null | Required | ISO 8601 timestamp. Format: date-time. |
| created_at | string | null | Required | ISO 8601 timestamp. Format: date-time. |
Errors and recovery
| Code | HTTP | Recovery | Retry policy |
|---|---|---|---|
| INVALID_API_KEY | 401 | Use a current kc_test_ or kc_live_ secret key on /api/v1. Use a pk_test_ or pk_live_ publishable key only on browser checkout. | After fixing credentials. |
| INSUFFICIENT_PERMISSION | 403 | Grant the required scope or use a suitably scoped key. Dashboard organization roles and staff permissions are separate. | After permission changes. |
| RATE_LIMIT_EXCEEDED | 429 | Honor Retry-After, use bounded exponential backoff with jitter, and reduce concurrent requests. Read X-RateLimit headers. | Yes, after Retry-After; preserve write idempotency. |
| METHOD_NOT_ALLOWED | 405 | Use the method documented for the endpoint, such as POST for verification and refunds. | After fixing the method. |
| INTERNAL_ERROR | 500 | Record X-Request-Id, timestamp and sanitized request context. Reconcile unknown payment outcomes before retrying a write; contact support if it persists. | Safe reads: bounded backoff. Writes: reconcile first. |
| VALIDATION_FAILED | 422 | Read error.details when present, correct the named fields and retry only after correction. Validation messages may describe state/capability errors without field details. | No automatic retry. |
| DUPLICATE_REFERENCE | 409 | Retrieve that payment and reconcile its state. For a genuinely new attempt, use a new unique reference and operation key; do not create a second charge just because a response was lost. | Reconcile first. |
| PROVIDER_NOT_CONNECTED | 422 | Connect a provider, choose the project default, or explicitly pass provider. An account shared with another project still needs its own assignment here. | After configuration changes. |
| INVALID_PROVIDER | 422 | Choose a supported connected provider. Refund/direct-checkout capabilities can differ from simple payment creation. | After configuration or provider selection changes. |
| INVALID_CURRENCY | 422 | Use a supported currency/provider pair. Do not silently convert amounts or currencies. | After correcting the request. |
| PROVIDER_AUTHENTICATION_FAILED | 502 | The merchant must check/rotate the provider credentials in the dashboard. Do not expose those secrets to your application users. | After credential correction; reconcile existing payment first. |
| PROVIDER_REQUEST_FAILED | 422 | Read the safe message, inspect the payment’s failure details and correct the request or provider account. Never repeatedly retry a business rejection. | Only after resolving the cause. |
| PROVIDER_UNAVAILABLE | 503 | Check the existing payment and verify its state before starting another attempt. Retry safe reads with backoff; preserve operation identity for retried writes. | Conditional: reconcile unknown outcomes first. |
| PLAN_LIMIT_REACHED | 402 | Review the active plan, project/usage allowances and billing status with the organization owner. | After resolving plan/billing restrictions. |
| IDEMPOTENCY_KEY_REUSED | 409 | Reuse the exact original request to recover that operation, or use a new key only for a genuinely different operation. | Do not retry a changed body with that key. |
| IDEMPOTENCY_IN_PROGRESS | 409 | Wait briefly, retry with the same key and unchanged request, then reconcile by reference if the result remains uncertain. | Back off with jitter; preserve key/body. |