What this is
By default every verification and KYB lookup you trigger is billed to your partner account. To bill one of your own customers instead, create a contact and have it save a card.
This is not an ATO payment. Customer billing charges your customer for work you ran on them. To move money to the ATO, see ATO payments.
1. Create the contact
POST /v1/contacts
X-Partner-Id: YOUR_PARTNER_ID
Content-Type: application/json
{
"name": "Acme Pty Ltd",
"externalId": "your-key-42",
"contactEmail": "accounts@acme.example"
}
name is the only required field. Send the contact's own
business details, not their end users' personal information.
externalId is your opaque key, immutable after create.
A duplicate returns 409 CONTACT_EXTERNAL_ID_EXISTS.
The contact starts in
billingStatus: NO_PAYMENT_METHOD.
2. Send the card link
POST /v1/contacts/{id}/card-link
X-Partner-Id: YOUR_PARTNER_ID
Content-Type: application/json
{ "redirectUrl": "https://app.example.com/card-done?contact={contactId}" }
This mints a single-use guest URL. Send it to the contact through your own channel. On the hosted page the contact accepts the merchant-initiated billing authorisation and saves a card.
The url is returned once and is never
re-issued.
Capture it from this response.
Optionally pass a redirectUrl in the body to send the
contact back to your platform when they finish. They are redirected
to it with ?status=success or
?status=removed appended. Include a
{contactId} placeholder to get the contact id back:
https://app.example.com/card-done?contact={contactId}
3. Wait for the card
Wait for the contact.card.saved webhook, or poll
GET /v1/contacts/{id} until
billingStatus: ACTIVE.
Card management is guest-only. The contact saves, replaces and removes its card exclusively through the minted link. There are no partner payment-method endpoints.
4. Bill the work
Pass contactId on
POST /v1/verifications or
POST /v1/business-lookups. Admiin then runs the contact
billing gate.
Charges settle against the contact's card once the unpaid balance crosses the $50 threshold. Any smaller non-zero balance is collected at the start of each month.
Each settlement charges the net batch total plus a
1.9% card processing fee. The
contact.billing.* webhooks report
totalAmountCents (net), feeAmountCents and
grossAmountCents, which is what hit the card.
When the gate blocks
A contact-billed call returns one of these when the gate blocks it. No verification or lookup is performed, and no charge is written.
code |
Meaning |
|---|---|
PAYMENT_METHOD_REQUIRED |
The contact has no saved card yet. |
CONTACT_BILLING_BLOCKED |
Billing is blocked on this contact. |
CONTACT_NOT_FOUND |
No such contact for this partner. |
CONTACT_INVALID |
The contact is not in a billable state. |
Reconciling charges
GET /v1/charges?contactId={id}
X-Partner-Id: YOUR_PARTNER_ID
Lists the individual charges behind a contact's settlements. Narrow
it with billingRunId, paymentStatus or a
fromDate and toDate range.
For a spreadsheet rather than JSON:
POST /v1/charges/export
X-Partner-Id: YOUR_PARTNER_ID
Synchronous. The CSV comes back in the 200 body as
text/csv. There is no job to poll.
Pricing
GET /v1/pricing
X-Partner-Id: YOUR_PARTNER_ID
Returns your fee catalogue, with any partner-specific overrides
layered over the defaults.
It is a catalogue, not a rate card. Check
billable on each line before you publish it to your own
customers.
Archiving a contact
POST /v1/contacts/{id}/archive
X-Partner-Id: YOUR_PARTNER_ID
Soft-archives the contact. It is refused with
409 CONTACT_HAS_UNPAID_BALANCE while money is still
owed, unless you pass ?force=true.
Billing webhooks
Track the lifecycle with the contact.card.* and
contact.billing.* events. See
Webhooks for signature validation, and
the contact billing events in the
API reference.