Lightning Payroll Partner Checkout Back to API documentation →
Partner integration guide

Discover, preview, execute.

Lightning Payroll's partner checkout API for reseller integrations where the authenticated caller is both an api_admin customer and in customer_group_id = 9. Discover current products and pricing, check identifiers are free, price an order before committing, then create the customer, company, order and subscription in one idempotent call.

14 endpoints 2 countries dry-run preview free trials idempotent execute

00Read this first

Partner checkout builds on the platform-wide API admin and OAuth flows. It is written for external developers who cannot inspect the source code. Read these guides before implementing checkout:

Those guides explain how to obtain access tokens, configure scopes, and authenticate as an API admin reseller.

01What partner checkout does

Partner checkout lets an eligible reseller create and manage a billed-account signup on behalf of a new client. The API can:

  • discover which products and add-ons are currently valid for the reseller
  • list allowed billing countries and zone codes
  • check whether an email, ABN, or NZ employer IRD number is already in use
  • preview pricing and validation results before creating anything
  • create the customer, address, company record, order, subscription, add-ons, totals, and post-pay record
  • optionally send the client a welcome email with a password-reset link
  • replay a previously successful execute request safely by idempotency key
  • list, inspect, and cancel orders created by that reseller
  • start a client on a free trial with no order and nothing to pay, then convert that trial into a paid subscription later (see "Free Trials")

02Base URL and authentication

  • Base path: /api
  • Auth header: Authorization: Bearer <access_token>
  • Caller must be an API admin reseller in customer group 9

If the authenticated customer is not an API admin, or is not in group 9, the API returns 403.

03OAuth scopes

PurposeScope
Product discoverypartner.checkout.preview or partner.checkout.write or partner.checkout.cancel
Availability checkspartner.checkout.preview or partner.checkout.write or partner.checkout.cancel
Zone discoverypartner.checkout.preview or partner.checkout.write or partner.checkout.cancel
Preview order (dry_run=true)partner.checkout.preview
Execute order (dry_run=false)partner.checkout.write
Cancel orderpartner.checkout.cancel or partner.checkout.write
List or inspect past ordersAny partner checkout scope above
Create a free trialpartner.checkout.write
List trialsAny partner checkout scope above
Cancel a trialpartner.checkout.cancel or partner.checkout.write
List client renewalsAny partner checkout scope above
Change a client's renewalpartner.checkout.write
Look up a clientAny partner checkout scope above

openid is still required on the OAuth authorization request. Legacy openapi remains accepted in the wider OAuth flow.

04Recommended integration sequence

1

GET/api/partner-checkout/options

2

GET/api/partner-checkout/zones

3

GET/api/partner-checkout/availability?email=...&abn=...

or ...&irdNumber=...

4

POST/api/partner-checkout/orders with dry_run=true

5

Same call again with the same business data, dry_run=false, and an Idempotency-Key

6

Persist the returned customer_id, order_id, and subscription_id

7

Use GET/api/partner-checkout/orders and GET/api/partner-checkout/orders/{order_id} for reconciliation and support

8

Use POST/api/partner-checkout/orders/cancel only when the reseller needs to cancel a qualifying order

To let the client evaluate the product before paying, start at "Free Trials" below instead of step 4, then come back to steps 4 and 5 with end_customer_id when the trial ends.

Do not hardcode product IDs, add-on IDs, pricing, or zone codes. Discover them dynamically.

05Country, identifier & address rules

Only Australia (AU) and New Zealand (NZ) are supported. Country is inferred from the company identifier:

Australia

  • Send company.abn

New Zealand

  • Send company.ird_number
  • You must provide exactly one of abn or ird_number.
  • billing_address.country_code is not accepted in requests.
  • billing_address.company is not accepted in requests.
  • customer.password is not accepted in requests.
  • billing_address.zone_code must match the inferred country.
  • zone_code is case-insensitive in the request and normalized to uppercase.
  • If a zone is invalid, the API returns 422 and includes the valid codes for that country in the error detail.

06Endpoint reference

1. Discover products and pricing

GET/api/partner-checkout/options

Use this endpoint to discover the current subscription products, add-on products, pricing, required scopes, and reseller-specific mandatory requirements.

Important response areas:

  • subscription_products: valid base products for order.product_id
  • add_on_products: optional or required add-ons
  • mandatory_requirements.training_session_product_id: mandatory training session product expected during signup
  • mandatory_requirements.training_session_product_name / mandatory_requirements.training_session_prices: display name and per-country (AU/NZ) retail pricing for the mandatory training session line, so the setup fee can be shown before previewing an order. These match the one-off training_session line returned by preview/execute (full retail price, no partner discount, RRP equal to its own price)
  • mandatory_requirements.required_owned_add_on_keys: branded add-on families this reseller must include for new client signups
  • per_employee_minimum: minimum quantity for per-employee base products
  • show_per_employee_pricing: whether this reseller can use per-employee plans
  • prices: country-specific partner pricing and, where available, retail RRP comparison values

2. Discover zones

GET/api/partner-checkout/zones

Use this endpoint to fetch the supported countries and valid zone_code values for billing addresses. The response currently includes AU and NZ. Use the returned zone.code values as billing_address.zone_code.

3. Check availability

GET/api/partner-checkout/availability

Use this endpoint to check whether one or more proposed identifiers are already in use.

Query parameters: email, abn, irdNumber. You can send any combination of these in one request, but at least one must be provided.

Each result includes provided, normalized, valid_format, exists, available, message, required_owned_add_ons, has_all_required_owned_add_ons.

Normalization behaviour: emails are trimmed and lowercased; ABNs and IRD numbers are digit-normalized before validation.

4. Preview or execute an order

POST/api/partner-checkout/orders

Use this endpoint to preview validation and pricing with dry_run=true, or to create the full customer and order with dry_run=false.

Headers: Authorization: Bearer <token>, Content-Type: application/json, Idempotency-Key: <unique value> required for dry_run=false.

Request body

Field pathTypeRequiredNotes
dry_runbooleanYestrue validates and prices only. false creates records.
end_customer_idintegerNoOrder for a client you already provisioned instead of creating a new one. Use this to convert a free trial. See "Ordering for an existing client".
send_customer_welcome_emailbooleanNoDefault false. Sends branded onboarding email with reset link when true. Ignored when end_customer_id is set.
customer.first_namestringYes1-32 chars
customer.last_namestringYes1-32 chars
customer.emailemailYesMust be unique
customer.phonestringYes3-32 chars
company.legal_namestringYes1-128 chars
company.abnstringConditionalAU only. Exactly one of abn or ird_number must be sent.
company.ird_numberstringConditionalNZ only. Exactly one of abn or ird_number must be sent.
billing_address.address_1stringYes3-128 chars
billing_address.address_2stringNoMax 128 chars
billing_address.citystringYes2-128 chars
billing_address.postcodestringYes2-10 chars
billing_address.zone_codestringYesMust be a valid zone for the inferred country
order.product_idintegerYesMust be a subscription product returned by /options
order.add_on_product_idsinteger[]NoOptional compatible add-ons; max 50 IDs
order.extra_company_qtyintegerNoAdditional company slots where supported
order.per_employee_qtyintegerConditionalRequired for per-employee products; must meet reseller minimum
order.add_free_trial_monthbooleanNoDefault true. Adds one extra free month, so a monthly signup gets 2 months and an annual signup gets 13. Granted once per client: if they already had a free trial, it is not granted again. See "The free month is once per client".
order.subscription_end_datestring (ISO date)NoOptional aligned subscription expiry. Triggers prorated pricing on the base subscription and recurring add-ons. See "Subscription term alignment & prorating".
order.client_referencestringNoReseller's own reference, max 255 chars
order.metadataobjectNoUp to 50 keys and under 4 KB serialized
oauth_onboardingobjectNoOpt in to single-flow OAuth onboarding. See "OAuth single-flow onboarding" below.
oauth_onboarding.redirect_uristringConditionalRequired when oauth_onboarding is sent. Must exactly match a redirect URI registered on your OAuth client.
oauth_onboarding.scopestringNoDefault openid payroll.write. Must include openid (legacy openapi accepted) and payroll.write.
oauth_onboarding.statestringNoOpaque value echoed back to your redirect_uri. Auto-generated if omitted.

Business rules

  • Duplicate customer email, ABN, and IRD values block checkout, unless they belong to the client named by end_customer_id.
  • Unknown fields are rejected with 422.
  • Base product_id must be a subscription product, not an add-on.
  • Per-employee products are only allowed when the reseller is configured for them.
  • Non-per-employee products reject per_employee_qty.
  • Per-employee products reject extra_company_qty.
  • extra_company_qty may be capped to the plan limit; when capped, a warning is returned.
  • Extra-company add-ons must not be passed directly in add_on_product_ids; use extra_company_qty.
  • Every chosen add-on must match the base product's billing cycle.
  • If the reseller owns required branded add-on integrations, the corresponding partner add-on products must be included.
  • A mandatory training session product is always added to the order.
  • When subscription_end_date is supplied, the base subscription and recurring add-ons are prorated; the training session is still charged at full price. See "Subscription term alignment & prorating".

Preview response

When dry_run=true, nothing is created. The response still contains the fully resolved pricing and address information:

  • mode = "preview"
  • validated = true
  • customer_id, address_id, order_id, and subscription_id are null
  • order_lines shows the base subscription, training session line, and any add-ons
  • totals shows subtotal, tax, total, and any welcome-offer promo discount
  • warnings describes non-fatal conditions such as capped extra-company quantity or the free trial month
  • free_trial_month_applied says whether a free month was granted on this order

Execute response

When dry_run=false, the API creates: a new customer, a billing/shipping address, a company record with the ABN or employer IRD number, an order with payment_code = "partner_checkout_billed_account", order totals and order products, a subscription and any subscription add-ons, a post_pay record for billed-account invoicing, order history, optional promo assignment, and an optional subscription-trial record when the free extra month is used.

Important execute response fields: customer_id, address_id, order_id, subscription_id, order_status_id, emails_sent, idempotency_replayed.

The magic onboarding link (when oauth_onboarding is used) is never returned in the response. It is emailed only to the customer's verified address.

OAuth single-flow onboarding (oauth_onboarding)

Normally, provisioning a customer and getting an OAuth payroll.write token for them are two separate loops: you create the order, the customer sets a password from the welcome email, then they (or you) run the OAuth ceremony separately before you can create their payroll company.

Supplying an oauth_onboarding block collapses that into a single flow:

1

You send dry_run=false with oauth_onboarding.redirect_uri (and optionally scope / state).

2

The new customer is emailed a single-use, 24-hour magic link. Opening it signs them in without a password and drops them straight onto your OAuth consent screen.

3

With one click of consent, their browser is redirected to your redirect_uri with ?code=...&state=....

4

You exchange the code at POST /api/oauth/token for a payroll.write access token, then create their company with PUT /api/company/create.

No second login and no separate OAuth round-trip are required. After consent the customer is given the chance to set a password (or skip and do it later) before landing back on your callback.

Requirements and behaviour:

  • You must have a registered OAuth client on your account (a 1-to-1 client owned by your api-admin customer). The redirect_uri must exactly match one of its registered redirect URIs, and scope must include payroll.write. A dry_run=true request validates all of this without creating anything or sending email.
  • The magic link is emailed only to the customer's verified address and is never returned in the API response. Handing a partner a passwordless login link would bypass the consent this flow exists to capture.
  • The link is single-use and expires after 24 hours. Re-provisioning (a new order) issues a fresh link.
  • oauth_onboarding replaces the password-reset welcome email; send_customer_welcome_email is ignored when it is supplied.

See also the OAuth Authentication Guide for the token exchange this flow feeds into.

5. Cancel an order

POST/api/partner-checkout/orders/cancel

Use this endpoint to cancel an order previously created by the authenticated reseller.

  • Reseller must own the order.
  • Order must not already be cancelled.
  • Cancellation is only allowed within 60 days of the created customer's date_added.
  • partner.checkout.cancel is preferred, but partner.checkout.write is also accepted.

6. Inspect one order

GET/api/partner-checkout/orders/{order_id}

Returns the current status and history for one order created by the reseller, including totals and currency, customer and company details, subscription ID and expiry, cancel_window_closes, cancellable, and ascending history[].

7. List orders

GET/api/partner-checkout/orders

Lists only the orders created by the authenticated reseller, newest first. Query parameters: status, limit, offset.

8. Resend the onboarding link

POST/api/partner-checkout/orders/{order_id}/resend-onboarding-link

Re-issues and re-emails the single-use OAuth magic onboarding link for the customer created by one of your orders. Use this when the original link (valid for 24 hours, single-use) expired before the customer signed in.

Request body mirrors the oauth_onboarding block of POST /api/partner-checkout/orders:

POST .../resend-onboarding-link
{
  "oauth_onboarding": {
    "redirect_uri": "https://partner.example.com/oauth/callback",
    "scope": "openid payroll.write",
    "state": "partner-corr-abc123"
  }
}

The original redirect_uri/scope/state are never stored, so you must supply them again. They are re-validated against your current OAuth client, so a deleted client or a changed redirect URI fails 400 rather than minting a dead link.

Rules:

  • Requires partner.checkout.write.
  • Reseller must own the order.
  • The customer must have been provisioned with oauth_onboarding originally, and must not have completed onboarding yet (409 otherwise; for an already-active customer, use a standard password reset instead).
  • Any previously issued, unused link is invalidated; only the newest link works.
  • The magic link is emailed only to the customer's verified address and is never returned in the response.
  • If the email cannot be sent, the call returns 502 and no new link is issued.
200 OK
{
  "customer_id": 192601,
  "order_id": 192484,
  "email_sent": true,
  "expires_in_hours": 24,
  "scope_used": "partner.checkout.write"
}

07Free trials

A free trial lets your client evaluate Lightning Payroll before anyone is billed. It is deliberately not an order:

  • No order, order line, order total, or post-pay record is created.
  • Nothing appears on an invoice, and you are not billed.
  • The trial subscription is never renewed.
  • The trial runs for one calendar month from the day you create it.

When the trial ends, you place the real order with POST /api/partner-checkout/orders and pass the trial's customer_id as end_customer_id.

Trial sequence

1

POST/api/partner-checkout/trials provisions the client and emails them their access

2

Persist the returned customer_id and subscription_trial_id

3

GET/api/partner-checkout/trials to watch days_remaining

4

POST/api/partner-checkout/orders with dry_run=true and end_customer_id to confirm the price

5

Repeat with dry_run=false and an Idempotency-Key

The free month is once per client

order.add_free_trial_month defaults to true and adds one extra month to the subscription, so a monthly signup gets 2 months and an annual signup gets 13.

That free month is granted once per client. If the client has already had a free trial, whether from POST /api/partner-checkout/trials, a signup on our website, or an earlier order of yours that carried a free month, it is not granted a second time.

When that happens the request still succeeds. It does not error:

200 OK – no free month applied
{
  "free_trial_month_applied": false,
  "warnings": [
    "A free trial month was already provided for this customer (trial ended 2026-08-31), so no additional free month was applied to this order."
  ]
}

Read free_trial_month_applied rather than assuming, so you never quote a client 13 months and deliver 12. In practice this only triggers when you send end_customer_id, because a brand-new client has no prior trial and always receives the free month.

Ordering for an existing client

Send end_customer_id on POST /api/partner-checkout/orders to order for a client you already provisioned. Then:

  • No new customer record is created; the order attaches to that client.
  • The customer block is still required, and customer.email must match that client's email, otherwise the API returns 409. This stops an order being attached to the wrong account.
  • The company block is still required. Re-sending the same ABN or IRD number is accepted because that client already holds it.
  • The client's billing address is updated in place from billing_address.
  • Any still-running trial subscription is retired, so the paid plan's employee and company limits take effect immediately.
  • The password-reset welcome email is not resent, since the client already has a password. Use POST /api/partner-checkout/orders/{order_id}/resend-onboarding-link if they never signed in.

You may only pass an end_customer_id for a client you provisioned, meaning a trial you created or a client you already have an order for. Anything else returns 403. An unknown ID returns 404.

9. Create a trial

POST/api/partner-checkout/trials

Creates a client on a free one-month trial. Requires partner.checkout.write and an Idempotency-Key.

Field pathTypeRequiredNotes
customer.first_namestringYes1-32 chars
customer.last_namestringYes1-32 chars
customer.emailemailYesMust be unique
customer.phonestringYes3-32 chars
company.legal_namestringYes1-128 chars
company.abnstringConditionalAU only. Exactly one of abn or ird_number must be sent.
company.ird_numberstringConditionalNZ only. Exactly one of abn or ird_number must be sent.
billing_addressobjectNoOptional for a trial, since nothing is billed. Same fields and rules as the order endpoint. When omitted, no address record is created and a warning says so.
send_customer_welcome_emailbooleanNoDefault true. Emails the client a branded link to set their password and start the trial.
oauth_onboardingobjectNoSame block and behaviour as the order endpoint. Replaces the password-reset email with a single-use magic link into your consent screen.
POST /api/partner-checkout/trials
{
  "customer": {
    "first_name": "Alice",
    "last_name": "Nguyen",
    "email": "alice@example.com",
    "phone": "+61 7 3000 0000"
  },
  "company": {
    "legal_name": "Sunrise Hospitality Pty Ltd",
    "abn": "10000000000"
  }
}

Rules:

  • The company's ABN or IRD number is claimed at this point, so no other reseller can start a competing trial for the same entity, and your later order for that company is accepted.
  • A duplicate client email returns 409.
  • An ABN or IRD number already held by a different client returns 409.
  • Too many claim attempts in a minute returns 429.
  • The client's onboarding email carries your branding when you have white-label branding configured.
  • You receive no invoice or confirmation email, because nothing was charged.
200 OK
{
  "scope_used": "partner.checkout.write",
  "warnings": [],
  "customer_id": 192601,
  "address_id": null,
  "subscription_id": 88214,
  "subscription_trial_id": 26411,
  "customer_email": "alice@example.com",
  "company": {
    "legal_name": "Sunrise Hospitality Pty Ltd",
    "abn": "10000000000",
    "ird_number": null,
    "country_code": "AU",
    "currency_code": "AUD"
  },
  "trial_start_date": "2026-07-31",
  "trial_expiry": "2026-08-31",
  "days_remaining": 31,
  "site_mode": "au",
  "emails_sent": true,
  "idempotency_replayed": false
}

10. List trials

GET/api/partner-checkout/trials

Paginated list of the trials you created, newest first. Accepts any partner checkout scope.

NameTypeNotes
statusstringOptional filter: active, expired, or cancelled. Any other value returns 422.
limitinteger1-100, default 25
offsetintegerDefault 0

Each row returns subscription_trial_id, subscription_id, customer_id, customer_email, company_name, status, trial_start_date, trial_expiry, days_remaining, cancelled_at, converted, and converted_order_id.

  • status is active while the trial runs, cancelled if you ended it early, and expired once it lapsed on its own.
  • days_remaining is zero or negative once the trial has ended.
  • converted is true once you have placed a paid order for that client, and converted_order_id names it.
  • Trials that started on our own website are not listed, because they are not yours.

11. Cancel a trial

POST/api/partner-checkout/trials/cancel

Ends a trial before its expiry date, for example when it was provisioned against the wrong entity. Requires partner.checkout.cancel or partner.checkout.write.

POST /api/partner-checkout/trials/cancel
{
  "subscription_trial_id": 26411,
  "reason": "Provisioned against the wrong entity."
}

Rules:

  • You may only cancel a trial you created (403 otherwise), and an unknown ID returns 404.
  • Cancelling twice returns 409.
  • The trial subscription's expiry is set to yesterday, so the client's access ends immediately.
  • The client record and the trial history are kept for audit; nothing is deleted.
  • Cancelling does not release the company's claimed ABN or IRD number, because that record can carry live accounting-integration connections. If you need to re-provision the same company under a different email address, contact support.
200 OK
{
  "subscription_trial_id": 26411,
  "subscription_id": 88214,
  "customer_id": 192601,
  "previous_trial_expiry": "2026-08-31",
  "trial_expiry": "2026-07-30",
  "cancelled_at": "2026-07-31T02:14:09",
  "reason": "Provisioned against the wrong entity."
}

08Managing renewals

Upgrades move a client up a tier immediately. Moving a client down a tier works differently: a mid-term downgrade is rejected at checkout, because a part-used higher plan is worth more than the lower plan being bought. What you can set is the plan a client renews onto.

For clients whose headcount moves seasonally, put them on a monthly billing cycle. Monthly does not change the downgrade rule, but it means a renewal comes around every month rather than once a year, so a client can move up when they need to and back down at the end of the month.

12. List upcoming renewals

GET/api/partner-checkout/renewals

Lists every client subscription you onboarded that has not yet renewed, oldest expiry first. Query parameters: limit (default 50, max 200), offset.

Each entry carries the current plan, is_monthly, auto_renew_on, and pending_renewal.

pending_renewal is null until a renewal order has been prepared. Those are created 30 days before expiry for annual subscriptions, and same-day as the previous renewal for monthly ones, so a monthly client nearly always has one you can change. Check it is non-null, and that its editable flag is true, before calling the update below.

13. Change a pending renewal

PATCH/api/partner-checkout/renewals/{subscription_id}

Sets the plan a client renews onto, and whether they renew at all. Supply at least one field.

PATCH /api/partner-checkout/renewals/{subscription_id}
{
  "renewal_product_id": 280,
  "auto_renew_on": true,
  "send_renewal_notification": true
}

Business rules:

  • The subscription must be one your account onboarded, else 403.
  • renewal_product_id edits the pending renewal order, so 409 if none exists yet or it is no longer pending.
  • The product must be one of your own subscription plans; add-ons, ad-hoc products and discount products are 422.
  • The line's price and tax resolve from the renewal order's billing country, falling back to your account's billing address; 409 if neither resolves to AU or NZ.
  • The line is repriced at your negotiated reseller rate.
  • Per-employee quantity is kept as it is, unless it falls below the client's per_employee_minimum (default 15), in which case it is raised to that floor.
  • auto_renew_on is stored against the end customer, so it applies to every subscription that customer holds. The response lists each affected subscription in auto_renew_affects_subscription_ids.
  • A short notification is emailed to you whenever the line actually changes. Set send_renewal_notification to false to suppress it, for example when your own users drive the change.

No Idempotency-Key is needed. Editing an existing renewal line is idempotent by construction.

09Looking up a client

14. Resolve a client

GET/api/partner-checkout/customers/{customer_id}

Resolves a customer id into who that client is and whether they are currently subscribed. Requires any partner checkout scope.

Use it to turn the customerId on a webhook payload, or one you stored at onboarding, into a name, an email and the plans the client holds right now.

You can look up three kinds of client: clients you onboarded through partner checkout, clients you started on a free trial, and clients who have authorised your OAuth client.

The last case is the one that matters if your integration connects to customers who already subscribe rather than selling to them. Those clients have no order attributed to you, but a live authorisation is enough on its own. Any other customer id returns 404, whether or not it exists, so this cannot be used to discover ids you were not already given.

Cancelling a trial or an order does not end the relationship, so those clients remain visible.

200 OK
{
  "customer_id": 12345,
  "customer_name": "Alice Nguyen",
  "company_name": "Acme Pty Ltd",
  "email": "alice@example.com",
  "has_active_subscription": true,
  "subscriptions": [
    {
      "subscription_id": 987,
      "product_id": 240,
      "product_name": "Enterprise",
      "expiry": "2027-03-01",
      "status": "active",
      "is_monthly": false,
      "is_trial": false,
      "auto_renew_on": true,
      "quantity": 15
    }
  ]
}

Notes:

  • has_active_subscription is the same check that decides whether a client's data is writable. When it is false, reads keep working but writes return 403, so testing this first separates a lapsed client from a genuine fault in your integration.
  • subscriptions lists the current term of each subscription, latest expiry first. It is empty for a client who has never subscribed.
  • status becomes lapsed on the day after expiry. A subscription expiring today is still active.
  • is_trial marks a free trial. A trial reads as active right up until it ends, so check this flag before treating a client as a paying customer.
  • A trial was never sold, so it has no order line behind it. On a trial, product_id, product_name, quantity and is_monthly are all null. Treat is_trial as the reason those fields are missing, rather than expecting a product.
  • auto_renew_on is stored against the customer rather than the subscription, so every entry carries the same value.

Access to this endpoint is not a substitute for the webhook stream. It answers "what is this client's state now", not "what changed".

10Idempotency

Idempotency only applies to execute requests where dry_run=false, and to trial creation.

  • Idempotency-Key is required.
  • Maximum length is 255.
  • Same key plus same payload returns the original successful execute response with idempotency_replayed=true.
  • Same key plus different payload returns 409.
  • Keys are shared across orders and trials, so do not reuse an order's key for a trial.

11Pricing and promo behaviour

  • Australia uses AUD.
  • New Zealand uses NZD.
  • /options can show both partner pricing and mapped retail RRP values.
  • Preview and execute responses can include a welcome-offer promo discount on the base subscription line.
  • Preview and execute responses return identical order_lines and totals for the same payload: preview is the price the partner agrees to, and execute persists those exact numbers.

Per-line GST and RRP disclosure

Every entry in order_lines includes the GST treatment that was applied and, where available, the retail comparison values:

FieldDescription
tax_rateTax rate applied to this line. 0.1000 for AU GST, 0.0000 for NZ (GST-Free Export).
currency_codeAUD or NZD.
unit_price_ex_taxPer-unit partner price excluding tax. AU base.
unit_price_inc_taxPer-unit partner price including tax. AU = ex_tax × 1.10. NZ = ex_tax (no NZ GST line).
rrp_unit_price_ex_taxPer-unit retail price excluding tax. null when no retail counterpart exists (e.g. Figtree, per-employee REF SKUs). Prorated when subscription_end_date is set.
rrp_unit_price_inc_taxPer-unit retail price including tax. Same GST rules as unit_price_inc_tax.
rrp_line_total_inc_taxRRP × quantity, including tax.

LP-462 NZ pricing detail. Intellitron Pty Ltd is AU-GST-registered but not NZ-GST-registered, so sales to NZ are GST-Free Exports. To keep the numeric advertised price identical across AU and NZ, the NZ ex-tax price is uplifted by AU GST. NZ tax_rate stays 0.0000.

AU customer

  • Pays $1100 inc-tax for the product

NZ customer

  • Pays $1100 ex-tax, $0 GST, $1100 inc-tax

The numeric advertised price is the same; the GST treatment differs.

12Subscription term alignment & prorating

Partners often need a new Lightning Payroll subscription to expire on the same date as the partner's own renewal cycle. Supply order.subscription_end_date to align the term and have the base subscription and recurring add-ons prorated to that window.

Rules:

  • subscription_end_date must be strictly after today.
  • For annual base products it must be less than 365 days from today.
  • For monthly base products it must be less than 30 days from today.
  • Out-of-range values are rejected with 422.
  • The mandatory training session line is always charged at full retail; it is one-off labour, not a recurring service.
  • The aligned date is recorded as the subscription's exact expiry; the next term renews at full price under normal renewal rules.

Prorata formula

Prorata formula (pseudocode, not JSON)
paid_days  = (subscription_end_date - today) - (30 if add_free_trial_month else 0)
factor     = max(0, paid_days) / term_days     # term_days = 365 annual, 30 monthly
unit_price = round(base_after_reseller_discount * factor)   # NZ uplift applied after

If paid_days <= 0 (e.g. a partner aligning a customer for a window smaller than the free trial month) the prorated lines are charged $0. The training session line remains at full price regardless.

Response fields

  • subscription_end_date: echoed when supplied, null otherwise.
  • prorata_factor: the multiplier applied to prorated lines, null when no alignment was requested.
  • warnings: includes a description of the prorata factor and any $0-floor or trial-month-consumed notes.

13Email behaviour

On successful execute:

  • The authenticated reseller receives a billed-account confirmation email.
  • If oauth_onboarding was supplied, the new customer receives a branded magic onboarding email (see "OAuth single-flow onboarding"); this replaces the password-reset welcome email.
  • Otherwise, if send_customer_welcome_email=true, the new customer receives a branded onboarding email with a password-reset link.

If email sending fails, the order can still succeed and emails_sent will be false.

If a magic onboarding link expires before the customer uses it, re-send a fresh one with POST /api/partner-checkout/orders/{order_id}/resend-onboarding-link (endpoint 8). Unlike execute, a resend whose email fails returns 502 and issues no new link.

14Error reference

StatusTypical causes
400Invalid product selection, invalid add-on combination, missing execute idempotency key
403Caller is not an eligible API admin reseller, missing required scope, or attempting to access another reseller's order
404Order not found
409Duplicate email, ABN, or IRD; reused idempotency key with a different payload; already-cancelled order; cancellation window expired; onboarding-link resend for a customer not provisioned with OAuth onboarding or who already completed onboarding
422Validation errors, missing required fields, unsupported extra fields, invalid ABN/IRD, invalid zone code
500Unexpected server-side failure
502Onboarding-link resend could not send the email; no new link was issued

15Production checklist

1

Discover products and zones dynamically from the API.

2

Run availability checks before previewing or executing.

3

Always preview before executing.

4

Use Idempotency-Key on every execute request.

5

Persist customer_id, order_id, and subscription_id for support and reconciliation.

6

Treat warnings as actionable integration signals.

7

Handle emails_sent=false separately from order success.

8

Use order lookup endpoints to reconcile retries and support cancellations.

16Related guides