DocumentationPOST Create a payment

Payments API

POST/payments

Create 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.

Markdown

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

FieldTypePresenceDescription
amountintegerRequiredInteger minor-unit amount. NGN 500.00 is 50000; JPY 500 is 500. Minimum 1.
currencystringRequiredThree-letter currency supported by the selected provider. Maximum 3 characters.
referencestring | nullOptionalUnique within this environment. Omitted, defaults to the new payment ID. Maximum 190 characters.
descriptionstring | nullOptionalPayment description. Maximum 255 characters.
callback_urlstring | nullOptionalHTTPS return URL. Verify payment server-side after the redirect. Maximum 2048 characters. Format: uri.
providerstring | nullOptionalProvider slug. Omit only when the environment has a default or exactly one active provider. Maximum 40 characters.
customerobjectOptionalSee the JSON example.
customer.emailstringRequiredRequired for nested customer requests. Maximum 190 characters. Format: email.
customer.namestring | nullOptionalMaximum 190 characters.
customer.phonestring | nullOptionalMaximum 32 characters.
customer_emailstringOptionalLegacy flat alternative to customer.email. Maximum 190 characters. Format: email.
customer_namestring | nullOptionalLegacy flat name. Maximum 190 characters.
customer_phonestring | nullOptionalLegacy flat phone. Maximum 32 characters.
metadataobject | array | nullOptionalYour 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 dataTypePresenceDescription
idstringRequiredGlobally unique Veyqo payment ID; use it for reconciliation.
object"payment"RequiredSee the JSON example.
referencestringRequiredMerchant reference unique within the project environment; defaults to the payment ID.
statusstringRequiredNormalized payment state. Do not fulfil pending, processing, failed or cancelled payments. Values: created, pending, processing, succeeded, failed, cancelled, refunded, partially_refunded.
amountintegerRequiredOriginal amount in the smallest currency unit.
amount_refundedintegerRequiredSuccessfully refunded amount in the smallest currency unit.
currencystringRequiredThree-letter currency code.
customerobjectRequiredSee the JSON example.
customer.emailstringRequiredCustomer email address.
customer.namestring | nullRequiredCustomer name.
customer.phonestring | nullRequiredCustomer phone.
descriptionstring | nullRequiredOptional description.
checkout_urlstring | nullRequiredProvider checkout URL for direct payments; may be null depending on the flow. Format: uri.
providerstringRequiredSelected provider slug.
provider_referencestring | nullRequiredReference the provider assigned, not your order reference.
metadataobject | arrayRequiredYour non-sensitive JSON metadata. Objects are recommended; do not store secrets or card details.
failure_codestring | nullRequiredMachine-readable payment failure reason, separate from API error codes.
failure_messagestring | nullRequiredHuman-readable payment failure reason.
authorized_atstring | nullRequiredISO 8601 timestamp. Format: date-time.
succeeded_atstring | nullRequiredISO 8601 timestamp. Format: date-time.
failed_atstring | nullRequiredISO 8601 timestamp. Format: date-time.
created_atstring | nullRequiredISO 8601 timestamp. Format: date-time.

Errors and recovery

CodeHTTPRecoveryRetry policy
INVALID_API_KEY401Use 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_PERMISSION403Grant the required scope or use a suitably scoped key. Dashboard organization roles and staff permissions are separate.After permission changes.
RATE_LIMIT_EXCEEDED429Honor 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_ALLOWED405Use the method documented for the endpoint, such as POST for verification and refunds.After fixing the method.
INTERNAL_ERROR500Record 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_FAILED422Read 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_REFERENCE409Retrieve 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_CONNECTED422Connect 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_PROVIDER422Choose a supported connected provider. Refund/direct-checkout capabilities can differ from simple payment creation.After configuration or provider selection changes.
INVALID_CURRENCY422Use a supported currency/provider pair. Do not silently convert amounts or currencies.After correcting the request.
PROVIDER_AUTHENTICATION_FAILED502The 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_FAILED422Read 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_UNAVAILABLE503Check 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_REACHED402Review the active plan, project/usage allowances and billing status with the organization owner.After resolving plan/billing restrictions.
IDEMPOTENCY_KEY_REUSED409Reuse 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_PROGRESS409Wait 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.