Skip to content
Docs / Payment links

Payment links

A payment link creates a transaction and hands you its checkout URL. It is a checkout session with the expiry taken off — same request, same hosted page, same transaction behind it — on a URL that waits as long as your customer does.

Both create one transaction for an amount you set, and both send the payer to the same hosted checkout. Two differences: how long the URL lives, and what it is addressed by.

Payment link Checkout session
Lifetime Until it is paid, however long that takes 24 hours, or until it is paid
Addressed by Its own transaction id. While it is unpaid the page shows the amount and your name to whoever opens it, as an invoice would; once paid it stops disclosing them and says only that it has been paid A one-time token. The id-addressed page answers 404 for a session, paid or not
The URL Safe to commit to paper: it is still live when the invoice is found in a drawer Not meant to be written down — it is gone by tomorrow
Reach for it when An invoice you email, a reminder you post, a QR code on a statement — the payer gets to it in their own time A customer you are redirecting out of your own checkout right now, where a URL that expires is one less thing left lying around
One link, one payment A link is one transaction, so it takes one payment. It never charges twice: once it is paid, the page stops asking for money. The first time the payer is returned to your redirectUrl; if they open the URL again from the same browser — a bookmark, the Back button, the emailed invoice next week — they get a page telling them it is already paid, with your URL offered as a link they can follow rather than one they are thrown at. That way a revisit does not look like a fresh payment to whatever you have watching redirectUrl.

If you need the same amount from several people, create a link each — that is also the only way to tell afterwards who paid which.

Before you start

Every endpoint below needs Authorization: Bearer <accessToken>. Tokens last one hour and come from POST /api/token — see Authentication. The base URL is https://app.flik.co.nz.

Flik checks that your organisation can actually take the payment method you ask for, at the moment you create the link. That is deliberate: a link that cannot be paid is worse than no link, because you find out only when someone tries.

  • card or both needs card payments configured on your organisation.
  • open_banking or both needs two things: an approved open banking application, and a settlement account on the organisation. The application is what lets Flik ask a bank to move the money; the settlement account is where it lands. Having one without the other is a common state and it fails.
  • Open banking is New Zealand dollars only. If your organisation settles in AUD, only card works.
These are fixed in the portal Nothing in your code will clear an eligibility error. Someone has to add the bank account, finish the application, or switch card payments on in the Flik portal. See Errors for the full list of codes.
post /api/payment-links

This creates a transaction and returns the checkout URL for it. The body is a checkout session's body, field for field — if you have written one of these calls you have written both.

What comes back is different in two ways. A session's URL dies in 24 hours; this one does not, which is why it can go in an emailed invoice or a printed reminder and still work a fortnight later. And a session's URL carries a one-time token, while a link's is addressed by its own id — the id in the response — so the portal can show it again whenever you need to re-send it.

Nothing about it changes after this call — the amount, the reference and the webhook address are fixed for the life of the transaction. That is what makes the URL safe to commit to paper.

Store url when you get it. Reading the link back over the API gives you its status, not the URL, so this response is where your integration captures it. Treat it as returned rather than constructed — the shape is Flik's to change — and if it is lost, the link's page in the portal shows it again.

Headers

Authorization string Required
Bearer <accessToken>. See Authentication.
Idempotency-Key string Optional
Makes the request safe to retry for 24 hours. Strongly recommended — without one, a retried create sends your customer a second link for the same invoice. Same semantics as on a checkout session; see Idempotency.

Body

type string Required
Send single — one payment rather than a recurring series. Recurring is not implemented on this endpoint, so a link 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, and what you reconcile against. 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> to it — the same id this call returned, so you can read the link straight back. Query parameters of your own are preserved.

That id is not proof of payment. Anyone can put an id in a URL. Read the link and render from what comes back.

checkoutMethod string Required
open_banking, card, or both. Checked against your organisation as the link is created — see Before you start.
webhookUrl string Optional
Where Flik posts this link's events. Must be HTTPS and publicly reachable. Omit it and this link notifies nobody — it cannot be added once the link exists. See Webhooks.
foreignTransactionId string Optional
Your own identifier for this payment — an invoice id, an order number. Returned on reads and included in every webhook, so you can match a payment to your records 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.
curl -X POST https://app.flik.co.nz/api/payment-links \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "type": "single",
    "amount": { "total": 250.00 },
    "creditorReference": {
      "reference": "INV-1001",
      "particulars": "Acme Ltd",
      "code": "ORD-5591"
    },
    "checkoutMethod": "both",
    "webhookUrl": "https://example.org/flik/webhook",
    "foreignTransactionId": "invoice-1001"
  }'
200 OK
{
  "id": "9f2c1a70-4de8-4b6a-9c31-52a7b0e4d183",
  "status": "created",
  "url": "https://app.flik.co.nz/checkout/transaction/9f2c1a70-4de8-4b6a-9c31-52a7b0e4d183",
  "code": "ORD-5591",
  "amount": { "total": 250.00, "currency": "NZD" },
  "reference": "INV-1001",
  "checkoutMethod": "both",
  "foreignTransactionId": "invoice-1001",
  "testMode": false,
  "createdAt": "2026-09-12T02:14:09Z"
}

Response fields

id string
The transaction. Store it against your invoice — it is how you read the payment back, what appears in the portal and in reporting, and what Flik support will ask for. A checkout session wraps this in a cs_ id; here there is no wrapper, so the transaction is the object.
status string
created on a new link. See Statuses.
url string
The hosted checkout: the string to send, print or turn into a QR code. Opaque, and returned only here — see above.
code string
The statement code — yours if you sent one, otherwise the FLIK-00042 Flik generated. What your customer quotes when they call you.

Errors

Status Code When
400 validation_failed A field is missing, the wrong type, or out of range: a missing or non-positive amount.total, more than two decimal places on it, a creditorReference field over 12 characters, or a redirectUrl that is not a URL. Identical rules to a checkout session.
400 currency_mismatch amount.currency is not your organisation's settlement currency. Flik rejects rather than repricing.
400 idempotency_error The Idempotency-Key was used before with a different body.
400 card_not_enabled You asked for card or both and card payments are not configured.
400 open_banking_not_enabled You asked for open_banking or both and the open banking application is not approved.
400 settlement_account_missing A bank rail was asked for and the organisation has no settlement account.
400 open_banking_currency_unsupported Your organisation settles in AUD, which the bank rail cannot carry. Use card.
401 unauthorized No Authorization header, or not in the form Bearer <token>.
401 token_expired The token has expired. Fetch a new one and retry, once.
401 invalid_token The token could not be verified — usually truncated or double-prefixed.
401 unknown_api_key The API key the token was issued for no longer exists. A new token will not help.
500 internal_error Flik's end failed. Retry with backoff.
get /api/payment-links/{id}

This is how you find out whether the link has been paid. Everything create returned comes back with current values, plus particulars, transactionType, foreignTransactionId, createdAt and updatedAt — with one exception.

There is no url. The API returns it once, at create, and this read is for the link's status. Capture it from the create response; the portal shows it again if you need to re-send it.

status runs created → pending → completed or failed, exactly as a session's does. There is no expired: that is the whole point of a link. See Statuses.

checkoutMethod is what you offered; transactionType is the rail the money actually moved on. For a both link that is how you learn what your customer chose.

It is only meaningful once status is completed. Before that it reads open_banking on every link, whatever you set checkoutMethod to.

Read it, but do not sit in a loop on it This read is what you fulfil on, and for most payers you make it when they come back to your redirectUrl. What does not work on a link is tight polling: an invoice can sit unpaid for a fortnight, and a loop that asks every few seconds spends two weeks learning nothing.

So for a link, pair the read with something that says when to make it — a sweep over your unpaid invoices on whatever cycle your business already runs on, or a webhookUrl that tells you the moment it lands. Either way the answer you act on comes from reading the link back. See Webhooks.

Where the id comes from

A link's id is its transaction id, and both of the things that tell you a payment happened hand it to you: the webhook body, as transactionId, and the redirect back to your site, as the ?transactionId= Flik appends to your redirectUrl. Either one can be read straight back — there is no mapping to keep.

Parameters

id string, path Required
The id from the create response — the transaction id, the same value the webhook and the redirect give you.

Someone else's link is a 404

Ask for a link that belongs to another organisation and Flik returns 404, not 403. That is on purpose. A 403 would confirm the id exists, which would let anyone holding a valid API key walk the id space and learn which links are real. Flik will not say anything about an id it will not serve you.

So a 404 means one of two things: the link does not exist, or it is not yours. Check the id under Payment Links in the portal before you assume it was deleted.

curl https://app.flik.co.nz/api/payment-links/7b3e4d21-9a6c-4f08-b512-6de3a9c47f10 \
  -H "Authorization: Bearer $TOKEN"
200 OK
{
  "id": "9f2c1a70-4de8-4b6a-9c31-52a7b0e4d183",
  "status": "completed",
  "code": "ORD-5591",
  "amount": { "total": 250.00, "currency": "NZD" },
  "reference": "INV-1001",
  "particulars": "Acme Ltd",
  "checkoutMethod": "both",
  "transactionType": "open_banking",
  "foreignTransactionId": "invoice-1001",
  "testMode": false,
  "createdAt": "2026-09-12T02:14:09Z",
  "updatedAt": "2026-09-14T21:06:44Z"
}

Errors

Status Code When
401 unauthorized No Authorization header, or not in the form Bearer <token>.
401 token_expired The token has expired. Fetch a new one and retry, once.
401 invalid_token The token could not be verified — usually truncated or double-prefixed.
401 unknown_api_key The API key the token was issued for no longer exists. A new token will not help.
404 not_found No such link, or it belongs to another organisation.
500 internal_error Flik's end failed. Retry with backoff.

Statuses

Status Meaning Webhook
created The link exists; nobody has paid yet. —
pending A payment is in flight — awaiting bank approval, or a card charge is processing. —
completed Paid. The URL stops taking money. checkout_session.completed
failed The last attempt did not succeed. Not terminal — the link stays live and the customer can open it and try again. There is deliberately no webhook for this; see Events. —

The same vocabulary a checkout session uses, minus expired — a link has no deadline to miss. Which leaves exactly one webhook a link can send, checkout_session.completed, on one shared event stream: a receiver written for sessions already handles links.

Next steps

  • Webhooks — a link can be paid at any time by anyone, so a webhook is the only way to learn about a payment without polling.
  • Errors — particularly the eligibility codes, which mean something needs configuring in the portal.
  • Checkout sessions — for when you know the amount.