Skip to content
Docs / Checkout sessions

Checkout sessions

A checkout session is one payment, by one customer, for an amount you set. Flik gives you a URL to send them to and tells you when they have paid.

A session's URL expires in 24 hours, which is right for a customer paying now and wrong for an invoice that sits in someone's inbox for a fortnight. For that, use a payment link — the same call and the same kind of URL, without the deadline.

Create a checkout session

post /api/checkout-sessions

Headers

Authorization string Required
Bearer <accessToken>. See Authentication.
Idempotency-Key string Optional
Makes the request safe to retry for 24 hours. See Idempotency. Strongly recommended — this call creates a payment.

Body

type string Required
Send single — one payment rather than a recurring series. Recurring is not implemented on this endpoint, so a session created as recurring is taken as a single payment.
amount object Required
What you are charging.
amount.total number Required
Greater than zero, at most two decimal places, maximum 10000. Major units — 25.00 is twenty-five dollars, not twenty-five cents.
amount.currency string Optional

NZD or AUD. Your organisation has a settlement currency and that is what the payment is taken in. Omit this and Flik fills it in.

If you do send it, it is checked. A currency that does not match your organisation is rejected with currency_mismatch rather than silently repriced — an AUD merchant still sending NZD out of habit should find out, not discover it at reconciliation.

creditorReference object Required
What your customer sees on their bank statement. The length limits come from the New Zealand banking system, not from Flik.
creditorReference.reference string Required
1–12 characters. Letters, digits, space, and ' . , @ _ -. Usually your invoice or order number.
creditorReference.particulars string Optional
Up to 12 characters. Letters, digits, spaces and hyphens. Typically your trading name.
creditorReference.code string Optional

Up to 12 characters, same character set as particulars. The third and last of the New Zealand bank statement fields, shown to your customer alongside the reference and particulars.

Omit it and Flik generates one — the code in the response, of the form FLIK-00042. Send your own if you reconcile against a code your accounting system already issues; it must be unique to you, and Flik does not check that.

redirectUrl string Required

Where Flik sends the customer after a successful payment. Flik appends ?transactionId=<uuid>&checkoutSessionId=cs_… to it, so your handler knows which session the customer is asking about. Query parameters of your own are preserved.

That id is not proof of payment. Anyone can put an id in a URL. Read the session and render from what comes back — paid, still pending, or failed.

checkoutMethod string Required
open_banking, card, or both. See Choosing payment methods.
webhookUrl string Optional
Where Flik posts this session's events. Must be HTTPS and publicly reachable. Omit it and this session notifies nobody — it cannot be added once the session exists. See Webhooks.
foreignTransactionId string Optional
Your own identifier for this payment. Returned on reads and included in every webhook, so you can match a payment to your order without storing Flik's ids.
description string Optional
Up to 255 characters. Shown against the transaction in the Flik portal. For your team, not your customer.
webhookUrl is per session There is no account-wide endpoint to fall back on. Set it on every session you want to hear about — easiest in whatever wraps your create call, so no individual call site can forget. See Webhooks.
curl -X POST https://app.flik.co.nz/api/checkout-sessions \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: order-5591" \
  -d '{
    "type": "single",
    "amount": { "total": 25.00 },
    "creditorReference": {
      "reference": "INV-1001",
      "particulars": "Acme Ltd",
      "code": "ORD-5591"
    },
    "checkoutMethod": "both",
    "redirectUrl": "https://example.com/thanks",
    "webhookUrl": "https://example.com/flik/webhook",
    "foreignTransactionId": "order-5591"
  }'
200 OK
{
  "id": "cs_7Hk2pQr4TnVxWy9BdLmE",
  "status": "created",
  "url": "https://app.flik.co.nz/checkout/s/uZ8kT2vQ...",
  "code": "ORD-5591",
  "amount": { "total": 25.00, "currency": "NZD" },
  "checkoutMethod": "both",
  "reference": "INV-1001",
  "testMode": false,
  "expiresAt": "2026-09-13T04:11:00Z"
}
400 Bad Request
{
  "error": {
    "code": "card_not_enabled",
    "message": "Card payments are not configured for this organisation",
    "param": "checkoutMethod",
    "doc_url": "https://flik.co.nz/docs/errors/#card_not_enabled"
  }
}

Response fields

id string
The session id, prefixed cs_. Store it against your order — it is how you read the session back, and what Flik support will ask for.
status string
created on a new session. See Statuses.
url string
The hosted checkout. Opaque — do not construct it, do not parse it. See The checkout URL.
code string
The statement code — yours if you sent one, otherwise the FLIK-00042 Flik generated. What your customer quotes when they call you.
expiresAt string
ISO 8601, UTC. 24 hours after creation.

Errors

Status Code When
400 validation_failed A field is missing, the wrong type, or out of range. details lists every one.
400 currency_mismatch amount.currency is not your settlement currency.
400 card_not_enabled card or both without card payments configured.
400 open_banking_not_enabled The bank rail without an approved open banking application.
400 settlement_account_missing The bank rail with no settlement account on the organisation.
400 open_banking_currency_unsupported The bank rail for an organisation that does not settle NZD.
400 idempotency_error The Idempotency-Key was already used with a different body.
401 unauthorized, token_expired, invalid_token, unknown_api_key Missing, expired, unverifiable, or orphaned credentials.
500 internal_error Flik-side failure. Retry with backoff and an Idempotency-Key.

Nothing is created when any of these is returned — an error means no session and no transaction, so a corrected retry is always safe.

Retrieve a checkout session

get /api/checkout-sessions/{id}

Read a session back to find out whether it has been paid.

Where the id comes from

{id} is the cs_ id. You never have to store it against your order to use it, because both of the things that tell you a payment happened carry it:

  • the webhook body, as checkoutSessionId
  • the redirect back to your site, as the ?checkoutSessionId= Flik appends to your redirectUrl

So a webhook handler and a redirect handler can each read the session straight from what they were just given.

curl https://app.flik.co.nz/api/checkout-sessions/cs_7Hk2pQr4TnVxWy9BdLmE \
  -H "Authorization: Bearer $TOKEN"
200 OK
{
  "id": "cs_7Hk2pQr4TnVxWy9BdLmE",
  "status": "completed",
  "code": "ORD-5591",
  "amount": { "total": 25.00, "currency": "NZD" },
  "reference": "INV-1001",
  "particulars": "Acme Ltd",
  "checkoutMethod": "both",
  "transactionType": "card",
  "testMode": false,
  "foreignTransactionId": "order-5591",
  "expiresAt": "2026-09-13T04:11:00Z",
  "createdAt": "2026-09-12T04:11:00Z",
  "updatedAt": "2026-09-12T04:13:22Z"
}
404 Not Found
{
  "error": {
    "code": "not_found",
    "message": "Checkout session not found.",
    "param": null,
    "doc_url": "https://flik.co.nz/docs/errors/#not_found"
  }
}

Response fields

Everything create returns except url, plus particulars, transactionType, foreignTransactionId, createdAt and updatedAt.

The payer URL is not returned again. Flik stores only a hash of the token inside it, so there is nothing to rebuild it from — and a read that handed back a working payment URL to anyone with an API key would be a second way in. Capture it at create.

checkoutMethod is what you offered; transactionType is the rail the money actually moved on — open_banking or card. For a both session that is how you learn what your customer chose.

Read it only once status is completed. Until a payment settles, transactionType reads open_banking on every session, including one you created as card — it records the rail that was used, and before anyone pays there is not one.

Sessions are scoped to your account A session belonging to another organisation returns 404, not 403. Flik will not confirm that an id it is unwilling to serve you exists at all.

Errors

Status Code When
401 unauthorized, token_expired, invalid_token, unknown_api_key Missing, expired, unverifiable, or orphaned credentials.
404 not_found No such session — or it exists and belongs to another organisation. The two are indistinguishable on purpose.
500 internal_error Flik-side failure. Safe to retry; this call changes nothing.

The checkout URL

url carries a high-entropy token rather than the transaction id. Three consequences worth designing around:

  • It expires after 24 hours. If you email a payment request and the customer opens it two days later, they get an expired page. Create a fresh session rather than storing a URL for later.
  • It stops disclosing details once paid. After a successful payment the URL redirects to your redirectUrl instead of showing the amount and reference again.
  • It is opaque. Do not build it yourself and do not parse an id out of it. Use id from the response.

The reason is that checkout URLs leak — into browser history, Referer headers, forwarded emails, screenshots. An expiring token limits what a leaked URL is worth.

Choosing payment methods

checkoutMethod Customer sees Your account needs
open_banking A list of banks; approves in their banking app An approved open banking application and a settlement account
card Card entry on the Flik checkout Card payments configured
both Chooses bank or card Both of the above

If your account is not set up for what you ask for, the request fails at creation with a 400. That is deliberate: better to fail when you create the session than to send a customer to a page with no way to pay. Those are fixed in the portal, not in your code.

Open banking is NZD only The bank rail settles in New Zealand dollars. If your organisation settles in AUD, open_banking and both are rejected with open_banking_currency_unsupported. Use card.

Idempotency

Creating a payment is not something you want to do twice because a connection dropped. Send an Idempotency-Key header with a value unique to the operation — your order id is usually the right choice.

Situation What happens
Key not seen before Request runs normally and the response is recorded.
Key seen, same body The original response is replayed. No second session is created. The reply carries Idempotent-Replayed: true.
Key seen, different body 400 idempotency_error. Reusing one key for two different payments is a bug, so Flik refuses rather than guessing.

Keys are remembered for 24 hours and are scoped to your organisation, so your order-1 can never collide with another merchant's. Only successful responses are replayed — if a request failed validation, fix it and retry with the same key.

One honest limitation The idempotency record is written after the session is created. If Flik fails in the gap between the two, a retry can create a second session. The window is small but it is not zero, so treat idempotency as greatly reducing duplicates rather than making them impossible.

Statuses

Status Meaning Webhook
created Session exists; nobody has paid yet. —
pending A payment is in flight — awaiting bank approval, or a card charge is processing. —
completed Paid. Fulfil the order. checkout_session.completed
failed The last attempt did not succeed. Not terminal — the session is live and the customer can try again, which most do within seconds. There is deliberately no webhook for this; see Events. —
expired 24 hours passed without payment. Create a new session. Expiry is something you read, not something Flik sends. —

Treat this list as open — handle an unrecognised status by not fulfilling, rather than by erroring.