Back to API documentation
# Partner Checkout Admin Endpoints Guide This guide documents Lightning Payroll's partner checkout API for reseller integrations where the authenticated caller is both: - an `api_admin` customer - in `customer_group_id = 9` It is written for external developers who cannot inspect the source code. ## Read This First Partner checkout builds on the platform-wide API admin and OAuth flows. Read these guides before implementing checkout: 1. `backend/docs/api_admin_setup_and_management_guide.md` 2. `backend/docs/oauth_authentication_guide.md` Those guides explain how to obtain access tokens, configure scopes, and authenticate as an API admin reseller. ## What 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 ## Base 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`. ## OAuth Scopes | Purpose | Scope | |---|---| | Product discovery | `partner.checkout.preview` or `partner.checkout.write` or `partner.checkout.cancel` | | Availability checks | `partner.checkout.preview` or `partner.checkout.write` or `partner.checkout.cancel` | | Zone discovery | `partner.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 order | `partner.checkout.cancel` or `partner.checkout.write` | | List or inspect past orders | Any partner checkout scope above | `openid` is still required on the OAuth authorization request. Legacy `openapi` remains accepted in the wider OAuth flow. ## Recommended 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. `POST /api/partner-checkout/orders` 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 Do not hardcode product IDs, add-on IDs, pricing, or zone codes. Discover them dynamically. ## Country, Identifier, and Address Rules - Only Australia (`AU`) and New Zealand (`NZ`) are supported. - Country is inferred from the company identifier: - send `company.abn` for Australia - send `company.ird_number` for New Zealand - 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. ## Endpoint Reference ### 1. `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.implementation_product_id`: mandatory implementation product expected during signup - `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. `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` - `NZ` Use the returned `zone.code` values as `billing_address.zone_code`. ### 3. `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 behavior: - emails are trimmed and lowercased - ABNs are digit-normalized before validation - IRD numbers are digit-normalized before validation ### 4. `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 path | Type | Required | Notes | |---|---|---|---| | `dry_run` | boolean | Yes | `true` validates and prices only. `false` creates records. | | `send_customer_welcome_email` | boolean | No | Default `false`. Sends branded onboarding email with reset link when true. | | `customer.first_name` | string | Yes | 1-32 chars | | `customer.last_name` | string | Yes | 1-32 chars | | `customer.email` | email | Yes | Must be unique | | `customer.phone` | string | Yes | 3-32 chars | | `company.legal_name` | string | Yes | 1-128 chars | | `company.abn` | string | Conditional | AU only. Exactly one of `abn` or `ird_number` must be sent. | | `company.ird_number` | string | Conditional | NZ only. Exactly one of `abn` or `ird_number` must be sent. | | `billing_address.address_1` | string | Yes | 3-128 chars | | `billing_address.address_2` | string | No | Max 128 chars | | `billing_address.city` | string | Yes | 2-128 chars | | `billing_address.postcode` | string | Yes | 2-10 chars | | `billing_address.zone_code` | string | Yes | Must be a valid zone for the inferred country | | `order.product_id` | integer | Yes | Must be a subscription product returned by `/options` | | `order.add_on_product_ids` | integer[] | No | Optional compatible add-ons; max 50 IDs | | `order.extra_company_qty` | integer | No | Additional company slots where supported | | `order.per_employee_qty` | integer | Conditional | Required for per-employee products; must meet reseller minimum | | `order.add_free_trial_month` | boolean | No | Default `true`. Adds one extra trial month. | | `order.client_reference` | string | No | Reseller's own reference, max 255 chars | | `order.metadata` | object | No | Up to 50 keys and under 4 KB serialized | #### Business rules - Duplicate customer email, ABN, and IRD values block checkout. - 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 implementation product is always added to the order. #### 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, implementation 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 #### 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 - 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` ### 5. `POST /api/partner-checkout/orders/cancel` Use this endpoint to cancel an order previously created by the authenticated reseller. Rules: - 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. `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` - ascending `history[]` ### 7. `GET /api/partner-checkout/orders` Lists only the orders created by the authenticated reseller, newest first. Query parameters: - `status` - `limit` - `offset` ## Idempotency Idempotency only applies to execute requests where `dry_run=false`. - `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` ## Pricing and Promo Behavior - 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 ## Email Behavior On successful execute: - the authenticated reseller receives a billed-account confirmation email - if `send_customer_welcome_email=true`, the new customer also 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`. ## Error Reference | Status | Typical causes | |---|---| | `400` | Invalid product selection, invalid add-on combination, missing execute idempotency key | | `403` | Caller is not an eligible API admin reseller, missing required scope, or attempting to access another reseller's order | | `404` | Order not found | | `409` | Duplicate email, ABN, or IRD; reused idempotency key with a different payload; already-cancelled order; cancellation window expired | | `422` | Validation errors, missing required fields, unsupported extra fields, invalid ABN/IRD, invalid zone code | | `500` | Unexpected server-side failure | ## Production 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.