Payment lifecycle & statuses

Every payment reports two independent axes — status, where the money is, and subStatus, what exactly is happening. Read both; never infer one from the other.

status tells you where the money is (pending → accepted → success, or failed); subStatus tells you what exactly is happening (awaitingPayment, confirming, overpaid, expired, …). They move independently.

The lifecycle

  1. Created. pending / created when an activation flow is configured (no address yet), otherwise pending / awaitingPayment with the deposit address issued and the payment window counting.
  2. Activated. With activationFlowSeconds set, the window starts counting only when you call POST /v1/payments/{paymentId}/activate or the customer opens the hosted page — so the timer never runs while the customer is still in your cart. Range: 1–3600 seconds.
  3. Paying. The first on-chain transfer moves the payment to pending / confirming. All transfers detected for the payment are listed at GET /v1/payments/{paymentId}/transactions.
  4. Accepted. Enough confirmations have accumulated to act on — ship, credit, deliver. The balance is credited, a payment.accepted webhook fires. Not final yet.
  5. Success. Irreversible; funds are withdrawable. payment.success fires. Terminal.
  6. Failed. Expired, cancelled, or invalidated without a valid payment. payment.failed fires. Terminal.

Statuses and sub-statuses

Values are the enumerations of the public specification; descriptions come from the same source. → PaymentStatusEnum, PaymentSubStatusEnum

statusFundssubStatus you can see under itWhat it means
pendingNot creditedcreated activated awaitingPayment confirming complianceHoldAwaiting funds, or funds seen and confirming. Nothing is credited yet. complianceHold means the transaction is held for compliance review.
acceptedCreditedcompleted overpaid underpaidSafe confirmations reached and the balance is credited. Act on it — ship, credit, deliver.
successCredited · withdrawablecompleted overpaid underpaidIrreversible and withdrawable. The sub-status records how the amount landed.
failedNot creditedexpired cancelled partiallyPaid complianceRejectedNo valid payment; terminal. partiallyPaid means a partial transfer arrived but never reached acceptance; complianceRejected means screening rejected it and nothing was credited.

Treat the status set as open

New sub-statuses may appear as the product grows. Read statuses from API responses instead of hardcoding exhaustive lists, and route anything unknown to a safe default in your code.

Timing you control

  • paymentWindowSeconds — how long the payment accepts funds: 300–86400 (5 minutes to 24 hours); project default applies when omitted.
  • activationFlowSeconds — grace period before the window starts counting: 1–3600. expiresAt always tells you the real deadline.
  • Both are fixed at creation.
create-payment.json
{
	"amount": "10000000",
	"asset": "USDT_ARBITRUM",
	"paymentWindowSeconds": 1800,
	"activationFlowSeconds": 600,
	"underpaymentTolerance": "500000"
}

Underpayment, overpayment, expiry

  • Underpaid: less arrived than requested. Set underpaymentTolerance (integer string, same unit as amount, ≥ 0 and < amount) to accept small shortfalls — dust, gas rounding — as fully paid. Default 0: exact amount required. Example: for "10000000" (10 USDT), a tolerance of "500000" forgives a 0.50 USDT shortfall.
  • Overpaid: more arrived than requested. The excess is credited alongside the expected amount; the payment completes as overpaid.
  • Expired: the window closed without sufficient funds — failed / expired. A partial transfer that never reached acceptance ends as failed / partiallyPaid; the amounts received are reported on the payment.
  • The dashboard and the API always show the real received amounts: amountReceived and amountConfirmed.

Idempotency and cancellation

  • externalId is your reference, echoed on the payment and its webhooks. A request with an externalId that already exists never creates a second payment — retries, timeouts, and double clicks are safe. The duplicate call is rejected with a 409 and the stable error code; fetch the existing payment instead.
  • POST /v1/payments/{paymentId}/cancel stops a payment that hasn't completed; valid only from early statuses, moves it to failed / cancelled.

Next: how Accepted and Success are decided → Settlement & finality.

Something unclear? payments@suward.com — we treat every integration question as a documentation defect.