Skip to content

Payments

A payment is a single charge attempt against an order. An order can have multiple payment attempts (e.g. card declined then re-tried).

Initiate a card payment

POST /pos/v1/orders/{order_id}/payments

Required scopes: tilt/payments:create

Request body

{
"method": "card",
"amount_cents": 5000,
"level3": { }
}
FieldTypeRequiredDescription
methodstringYes"card" (ACH and virtual payment form are separate flows)
amount_centsintegerYesAmount to charge. Must be ≤ order total remaining.
level3objectNoLevel 3 line-item data. May also be set on the order; payment-level takes precedence.

Response

{
"payment_id": "uuid",
"order_id": "uuid",
"status": "pending",
"amount_cents": 5000,
"method": "card",
"created_at": "2024-01-15T10:30:45Z"
}

Card payments are processed asynchronously. The initial status is pending. Subscribe to webhooks to receive payment.approved or payment.failed events, or poll /pos/v1/orders/{order_id} to check the final status.

Get a payment

GET /pos/v1/orders/{order_id}/payments/{payment_id}

Required scopes: tilt/payments:read

Response

{
"payment_id": "uuid",
"order_id": "uuid",
"status": "approved",
"amount_cents": 5000,
"method": "card",
"processor_reference": "valor-txn-ref",
"created_at": "2024-01-15T10:30:45Z",
"updated_at": "2024-01-15T10:30:47Z"
}

Void a payment

Voids a payment that is in pending or approved (pre-settlement) status.

POST /pos/v1/orders/{order_id}/payments/{payment_id}/void

Required scopes: tilt/payments:create

No request body required.

Response

{
"payment_id": "uuid",
"status": "voided",
"voided_at": "2024-01-15T10:35:00Z"
}

Payment statuses

StatusDescription
pendingSubmitted to processor, awaiting response
approvedProcessor approved; not yet settled
settledSettled in processor batch
declinedProcessor declined the card
failedProcessing error (network, timeout, etc.)
voidedVoided before settlement
refundedRefunded (post-settlement)
cancelledCancelled by operator before processor submission
expiredPayment link expired without completion

Polling vs. webhooks

Polling is acceptable for low-volume integrations. For high-volume or latency-sensitive flows, use webhooks.

Recommended polling interval: every 3 seconds, up to 60 seconds total. After 60 seconds a pending payment that has not transitioned should be investigated — contact support with the payment_id.