When to use this

Use the anonymous variant when:
  • You manage the end-user relationship entirely in your own system
  • You do not want kurnl to store subscriber PII
  • You use your own invoicing and billing — kurnl should not issue invoices on your behalf
With the anonymous variant, subscribers have no kurnl self-service portal access and kurnl cannot issue invoices on your behalf.
If you want kurnl to manage subscriber records and billing, use CKO-03 direct delivery instead.

How it works

When a subscriber selects a plan on the kurnl Marketplace, kurnl creates a checkout session and redirects them to your checkout URL with a session_id query parameter. Your checkout page reads this session to get the plan details, collects payment in your own system, then notifies kurnl to provision the port.

Step-by-step

1. Configure your checkout URL

Identical to direct delivery — see CKO-03 Direct: Step 1.

2. Read the session from the redirect URL

When the subscriber lands on your checkout page, the URL will contain ?session_id=<uuid>. Call the sessions endpoint to retrieve the plan details:
Response — 200 OK:
Use the returned plan_version_id to call GET /partner/marketplace/plans/{plan_version_id} if you want to display the plan name and price to the subscriber before they pay.
This read marks the session as reached, after which it no longer expires — so take it as your page renders. A session that is never read expires 2 hours after creation; an expired (or cancelled) session returns 410 Gone, and you redirect the subscriber back to the Marketplace to start a fresh checkout.

3. Collect payment in your own system

Display the plan details and handle the full checkout and payment in your own system. kurnl receives no customer data.

4. Call kurnl after successful payment

Once payment succeeds, notify kurnl to provision the port. You have two options: The simplest path. kurnl reads the plan, location, and provider directly from the session — you only need to supply your own internal subscription identifier. Any plan_version_id, location_hash, location_identifier, unit_id, mac_address, or delivery_mode in your payload is ignored — the session values win.
The building decides the path, as on /external-checkout/complete — not the delivery_mode of the session or of your payload. A first-line building, and a second- or third-line suite whose router is already online, is provisioned at once (ACTIVE); a shared first-line suite is held as an install job until the operator confirms the connection. Every other suite, and every suite in an unclassified building, gets PENDING_INSTALL and an operator install job. delivery_mode in the response reports the path taken (direct = provisioned, home_drop = install job). A direct order (a port, no suite) on a building that is not first line is refused with 422.
Send contact_email whenever the order needs a technician visit or a shipped router — a second- or third-line building, unless the suite’s router is already online, and an unclassified building, even when the suite’s router is online. Without contact_email such an order is refused with 422 contact_email_required — nothing is created; ask the household for an e-mail and send the order again. On a third-line building kurnl e-mails that address a link to a public booking page where the household picks the visit window. The address is stored on the subscription only; no customer record is created.
kurnl also uses contact_email for service mails to the household: an outage and its restoration, a shipped router, a received support ticket. If the provider switched a lifecycle template on for anonymous lines (welcome, plan changed, cancellation, …), those mails go there too. The greeting uses the name from the installation booking, or a neutral “Hello,” when there is none. The household never receives billing or account mails.

Option B — pass fields explicitly (legacy / non-Marketplace flows)

Use this when you are not going through a kurnl Marketplace session — for example, a direct link from your own website where you already know the plan and location.
Successful response — 200 OK (both options):
On the install path the response carries "delivery_mode": "home_drop", "subscription_status": "PENDING_INSTALL" and "job_id": null. Branch on these, not on the delivery_mode you sent. unit_confirmation_pending is true for an install-path order on a shared suite (several connections) whose connection the kurnl operator has not confirmed yet; the order stays PENDING_INSTALL meanwhile. No subscriber_id is returned — no subscriber record is created. Store the subscription_id to correlate with webhook events.

5. Handle webhook events

At provisioning.completed, a directly provisioned subscriber’s internet is live. On the install path, subscription.activated tells you the line went live, and install.booked tells you when the technician comes. Every event carries your external_subscription_id and is_anonymous: true. Optionally set a Backup notification e-mail on the dashboard Checkout page: kurnl then mails you each of these events too, in case a webhook is missed. See Webhooks. See Webhooks for signature verification and retry behaviour.

6. Read a subscription back

GET /partner/external-checkout/subscriptions/{external_subscription_id} returns a subscription’s current state addressed by your own identifier — status, whether it is anonymous, and, for an order that needs a technician, where the visit stands (workflow_state, appointment_at, on_hold, whether a booking link has gone out). This is how you show a home-drop customer a truthful status on your own thank-you page: such an order stays PENDING_INSTALL until the technician has been, and nothing about it changes for days.

7. Suspend and restore from your own billing

When your billing system puts a subscription on hold, call POST /partner/external-checkout/subscription-status with action: "suspend"; call it with "restore" when the customer pays again. Address the subscription by your own external_subscription_id plus service_provider_document_id — the same key you sent at completion. You never have to store a kurnl identifier.
A hold before the install is accepted, and holds the technician visit. A home-drop order is PENDING_INSTALL until the technician has been, so an on-hold in that window is the ordinary case — not an error. kurnl answers 200, cancels any booked visit, releases the slot and stops the booking link, while the subscription itself stays PENDING_INSTALL (it has not been installed, so it cannot be “suspended”).restore lifts the hold and sends a fresh booking link. Read subscription_status in the response rather than assuming SUSPENDED: before the install it stays PENDING_INSTALL in both directions.Once the line is ACTIVE, the same calls suspend and restore it normally.
restore is all-or-nothing: if kurnl cannot re-open the port it returns 409 and leaves the subscription SUSPENDED, rather than reporting it active while the customer is still offline. Check the status code, not port_changed.

8. Cancel when the customer leaves

Call the same endpoint with action: "cancel" and, if the service should run on, an end_date (YYYY-MM-DD, the first day without service). Without a date the line ends today: a live line runs through today, a line the technician has not yet installed ends at once and keeps its port reserved. A date in the past is refused with 422. You get subscription.cancelled, and later subscription.deprovisioned, as webhooks. A second cancel of the same line answers 409 with detail.code = "already_cancelled". See subscription-status.

9. Wi-Fi for the household

An anonymous line has no portal login, so the household sets its Wi-Fi on a page kurnl mails to it.
  • Automatic mail. kurnl sends the link once per line, to the contact_email you sent. It goes out when the router first makes contact. A line that goes live without that step gets it when it goes live. A line without contact_email gets no mail.
  • A new link. Call POST /partner/subscriptions/{id}/wifi-link when the household lost the mail. The link works once and for 7 days, and replaces the earlier unused one.
  • Set it yourself. Call PUT /partner/subscriptions/{id}/wifi with the network name and password. Use your own external_subscription_id with ?by=external_subscription_id.
The page shows only the provider’s branding, never the address or plan. kurnl sends no webhook for Wi-Fi changes.

Field reference

GET /partner/external-checkout/sessions/{session_id}

POST /partner/external-checkout/anonymous-complete


Error handling

On 5xx errors or network timeout: Safe to retry — the request is idempotent. The same external_subscription_id (or session_id) returns the existing subscription_id rather than creating a duplicate. Replays carry idempotent_replay: true and report the subscription’s real current status (e.g. PENDING_INSTALL for a home-drop order still awaiting install), with job_id: null.