external_checkout_url
with ?session_id=<id> appended, and your page reads the session to get the plan, location,
and delivery mode. See the flow guides: CKO-03 Direct,
CKO-03 Home-Drop, CKO-03 Anonymous.
Endpoints
A session you have read does not expire. The first time your checkout page reads a
session (
GET .../sessions/{session_id}), kurnl marks it redirected — the shopper has
arrived on your page — and from then on it is never reaped. It ends as completed or
cancelled. So a slow payment, a bank transfer, or a customer who finishes the next
morning can still be provisioned.
A session that is never read expires 2 hours after it is created. That window only
has to cover the hop from the kurnl Marketplace to your checkout page. Any endpoint that
touches such a session returns 410 Gone and marks it expired on the same call; that is
final, there is no way to revive it.
Session lifecycle: pending → redirected → completed | cancelled, or
pending → expired if your page never read it. expired and cancelled surface as 410.
Read the session once, early, when your checkout page renders — that read is what
protects the order. If your integration caches the session body and never calls kurnl
again before payment, the session stays
pending and can still expire under you.POST /partner/external-checkout/sessions
Creates apending session and returns your hosted checkout URL with session_id appended.
Auth: none required. If a signed-in kurnl user’s Bearer token is supplied, the session is
linked to that account (so it appears in their hub).
Request body
discount_code is a provider code or a kurnl operator discount code the shopper entered on
kurnl before the hand-over (see validate-discount-code).
A code that does not resolve answers 400 Invalid or expired discount code and no session is created.
Response — 201 Created
external_checkout_url points to the kurnl fake-Stripe page instead of your
configured checkout URL. If your provider has no external_checkout_url configured, the
endpoint returns 503.
With a discount_code, the hosted URL gains &discount_code=. Kurnl stores the code on
the session and lowers its own wholesale price when the order completes; it cannot show
your price. You are trusted to pass the discount on to the customer.
Creating or patching a session does not check the building’s bulk offer. The 422 bulk_plan refusal comes at external-checkout-complete and in the anonymous callback.
Error codes
GET /partner/external-checkout/sessions/
Returns the current state of a session, with the referenced plan, unit/building, and provider embedded for display on your checkout page. Auth:X-Webhook-Secret header.
Response — 200 OK
discount_code is the code the session carries, and operator_discount the kurnl operator
discount it resolves to (both null without one). Show the shopper promo_price_monthly for
duration_months months (null = for the life of the line), then list_price_monthly. If
the discount cannot be read, the call answers 503: retry.
operator_offers lists every kurnl operator discount the shopper may use on this session’s
building, with the monthly price it gives on this plan (promo_price_monthly for
duration_months months, null = life of the line, then list_price_monthly). Take the code
on your checkout page, match it against this list (case-insensitive), show the reduced summary,
and send it as discount_code on the completion call. operator_offers is always present: an
empty list means no kurnl operator code is valid on this building, so you can switch on the
field’s presence. If the offers cannot be read, the call answers 503: retry.
units.postal_unit is the unit’s own postal label as stored (e.g. "200" on connection
200-1), or null when the unit has none. It does not say whether the suite is shared: a
connection whose unit number is the suite itself (a bare "200" next to 200-1 and 200-2)
has null, and a unit alone in its suite may carry one. When the order is completed on a suite
with several connections, kurnl may place it on a different connection and hold it for the
operator (the completion response then has unit_confirmation_pending: true) —
see POST /external-checkout/complete.
expired and cancelled sessions are never returned as a body — they yield 410 Gone.
The embedded units.buildings object carries the full postal address of the building the
order is for — street, city, postal code, province and country. There is no buildings endpoint
on the partner API, so this embed is the only place that address reaches you: read it from the
session rather than expecting to look a building up by its id. The fields are nullable, because
kurnl holds addresses for buildings at varying levels of completeness — render defensively.
woocommerce_product_id is the intended product-mapping key for store integrations:
set it on the plan version to join a kurnl plan to the product in your own catalog.
The TTL check runs before authentication by design — an expired session
returns
410 even with a missing or wrong X-Webhook-Secret. It only ever applies to a
session still in pending.Error codes
PATCH /partner/external-checkout/sessions/
Upsell endpoint: swap the plan while the subscriber is still on your checkout page, or attach your own reference id. Onlypending/redirected sessions are patchable.
Auth: X-Webhook-Secret header.
Request body
Only these two fields are patchable — anything else (includingunit_id,
delivery_mode, service_provider_document_id) is rejected with 422.
Response — 200 OK
Returns the updated session object (same shape as GET).Error codes
Related
- Complete a session with subscriber PII:
POST /external-checkout/complete - Complete without sharing PII:
POST /external-checkout/anonymous-complete - Partner suspend/restore:
POST /external-checkout/subscription-status