Payment on arrival
With payment on arrival (PoA), a customer books without paying online and pays at the venue. The booking is confirmed straight away; its payment stays open until staff record it.
This page explains how a PoA booking moves through its life and which API calls touch it. For the staff side, see Today and check-in and Payments and refunds.
When PoA is offered
PoA is a product setting. A product offers it when Allow pay on arrival is on (isPoaAllowed on the product in the admin API).
Even then, the product's booking policy can refuse it:
| Booking policy | PoA allowed? |
|---|---|
| Default (no policy) | Yes |
payment_required | No |
instant_confirm_with_payment | No |
requires_approval | No. Every booking needs staff approval first (APPROVAL_REQUIRED). |
A refused PoA confirmation returns 409 with the error code POA_NOT_ALLOWED or PAYMENT_REQUIRED.
Status
A booking has two statuses: a lifecycle status (where the booking is) and a payment status (what has been paid). PoA uses both:
| Step | Lifecycle | Payment |
|---|---|---|
| Customer picks a time | hold | pending |
| Customer confirms with PoA | confirmed | pending |
| Customer arrives | arrived | pending, or paid if collected at check-in |
| Staff record the payment | unchanged | paid (or part_paid for a partial amount) |
| Visit ends | completed | paid |
See Status field conventions for all values.
A plain PoA booking has no payment deadline. It is not reminded or cancelled automatically; staff settle it on the day.
Deposit bookings
Pay on arrival is not offered for a product with a deposit, even if the product has pay on arrival turned on. The customer pays the deposit (or the full price) online, by Paytrail or card, and the booking becomes confirmed + part_paid. The rest is then collected one of two ways, set on the product's deposit:
- Online before the event (default): the customer gets a reminder email with a payment link before the balance is due.
- On arrival (
balanceDueMode: "on_arrival"): no reminder; staff collect the rest at the venue. Use this when you want a deposit up front and the rest paid on the day.
Staff can send the payment link again with POST /api/v1/admin/bookings/{bookingId}/resend-payment-link.
An API request for pay on arrival on a deposit product returns 409 DEPOSIT_REQUIRED. The former "pay later" option (payment_mode: "deposit_later"), which confirmed a deposit booking without any payment, has been removed.
API touchpoints
Confirm a hold with PoA
POST /api/v1/bookings/{id}/confirm
x-tenant-id: t_demo
{
"expected_pricing_hash": "…",
"payment_mode": "poa",
"guest_email": "customer@example.com",
"guest_name": "Alex Example",
"guest_phone": "+358401234567"
}
payment_mode is stripe (default) or poa; any other value returns 400 PAYMENT_MODE_NOT_SUPPORTED. The pricing hash comes from the hold; a changed price returns 409. See Bookings API for the hold and confirm flow.
Staff operations
These need a staff token with one of the listed permissions.
| Task | Call | Permissions |
|---|---|---|
| Find bookings | GET /api/v1/admin/bookings/search | admin:read |
| Check a customer in | POST /api/v1/bookings/{bookingId}/mark-arrived | bookings:checkin or bookings:write |
| Undo a check-in | POST /api/v1/bookings/{bookingId}/undo-arrival | bookings:checkin or bookings:write |
| Record a payment | POST /api/v1/bookings/{bookingId}/mark-paid | bookings:write or payments:record |
| Mark a no-show | POST /api/v1/bookings/{bookingId}/mark-no-show | bookings:checkin or bookings:write |
| Cancel | POST /api/v1/bookings/{bookingId}/cancel | Staff or the booking's customer |
| Resend a deposit payment link | POST /api/v1/admin/bookings/{bookingId}/resend-payment-link | admin:write |
GET /api/v1/admin/bookings/search filters on lifecycle status (status=confirmed), dates, site, product and customer. It has no payment-status filter; read paymentStatus in the results to find unpaid bookings.
Check in and collect in one step. mark-arrived takes an optional body:
{
"collectPayment": true,
"paymentMethod": "manual_cash",
"notes": "Paid at the desk"
}
paymentMethod is manual_cash, stripe_card_present or pos_integration. For a late arrival past the product's grace period, send "acknowledgeLateness": true.
Record a payment later. mark-paid takes an optional body:
{
"paymentMethod": "card_terminal",
"transactionId": "T-12345",
"amountPaid": 60.0,
"notes": "Paid after the session"
}
Cancel. cancel takes an optional reason and notes. Nothing was charged, so there is nothing to refund.