When to use this

Use CKO-03 direct delivery when:
  • The subscriber’s building is already wired — a switch port exists and is assigned to their unit
  • You run your own checkout page and want full control over the payment and signup experience
  • Your plan is listed on the kurnl marketplace and you want subscribers to start there
If the subscriber’s unit does not yet have a port installed, use CKO-03 home-drop delivery instead.

How it works

Step-by-step

1. Configure your checkout URL

In Dashboard → Settings → Integration, set your external_checkout_url. The kurnl Marketplace appends three query parameters when redirecting subscribers to your page:
ParameterDescription
location_hashOpaque 10-character token identifying the subscriber’s port location. Forward verbatim — do not parse or modify.
plan_version_idUUID of the plan version the subscriber selected on the marketplace.
service_provider_document_idYour provider UUID. Validates the request came from kurnl.
Example redirect:
https://checkout.yourcompany.com/internet?location_hash=ab12cd34ef&plan_version_id=f7e8...&service_provider_document_id=a1b2...
Read and store all three values on page load. You need them verbatim in the callback.

2. Collect payment and subscriber details

Run your normal checkout flow. Collect the subscriber’s:
  • Name, email, phone (optional)
  • Billing address
  • Payment method
kurnl does not receive payment details. You handle payment processing entirely on your side.

3. Call kurnl after successful payment

After payment succeeds, POST to /partner/external-checkout/complete:
curl -X POST https://api.kurnl.ca/api/v1/partner/external-checkout/complete \
  -H "Content-Type: application/json" \
  -H "X-Webhook-Secret: YOUR_WEBHOOK_SECRET" \
  -d '{
    "service_provider_document_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "plan_version_id": "f7e8d9c0-b1a2-3456-cdef-012345678901",
    "delivery_mode": "direct",
    "location_hash": "ab12cd34ef",
    "subscriber": {
      "email": "alice@example.com",
      "username": "alice",
      "password": "secure-password-123",
      "customer_type": "RESIDENTIAL",
      "mac_address": "AA:BB:CC:DD:EE:FF",
      "invoice_contact_detail": {
        "firstname": "Alice",
        "lastname": "Smith",
        "email": "alice@example.com",
        "phonenumber": "+16045551234"
      },
      "contract_contact_detail": {
        "firstname": "Alice",
        "lastname": "Smith",
        "email": "alice@example.com"
      },
      "invoice_address": {
        "street": "Maple St",
        "housenumber": "42",
        "postalcode": "A1B 2C3",
        "city": "Vancouver",
        "province": "BC",
        "country": "Canada"
      }
    }
  }'
Successful response — 200 OK:
{
  "job_id": "3d6e8f12-...",
  "subscription_id": "8c4a1b9e-...",
  "subscriber_id": "2f5d7a0c-...",
  "message": "Provisioning started"
}
Store the subscription_id and subscriber_id in your system — you’ll need them for any future subscriber management calls.

4. Handle webhook events

kurnl fires events to your configured webhook_url at each stage:
EventWhenKey fields
subscription.activatedImmediately after your callbacksubscription_id, subscriber_id, plan_version_id
provisioning.completed~30 seconds later, port is livejob_id, subscription_id
provisioning.failedIf SSH provisioning failsjob_id, subscription_id, error
At provisioning.completed, the subscriber’s internet is live. This is the right moment to send them a confirmation email. See Webhooks for signature verification and retry behaviour.

Field reference

Required fields

FieldTypeNotes
service_provider_document_idUUIDYour provider UUID — used to look up your webhook secret
plan_version_idUUIDFrom the marketplace redirect URL
location_hashstring (10 hex chars)From the marketplace redirect URL. Forward verbatim.
subscriber.emailstringKurnl account identifier. Existing accounts are reused.
subscriber.usernamestring (min 3)Display name
subscriber.invoice_contact_detailobjectSee contact detail fields below
subscriber.contract_contact_detailobjectCan be identical to invoice contact
subscriber.invoice_addressobjectSee address fields below

Optional fields

FieldTypeNotes
delivery_mode"direct" | "home_drop"Defaults to "direct"
subscriber.passwordstring (min 8)Only set for new accounts. Existing accounts keep their password.
subscriber.customer_type"RESIDENTIAL" | "BUSINESS"Defaults to "RESIDENTIAL"
subscriber.mac_addressstringEnables port security (static MAC binding) on the switch

Contact detail fields

FieldType
firstnamestring
lastnamestring
emailstring
phonenumberstring (optional)
companystring (optional)

Address fields

FieldType
streetstring
housenumberstring
postalcodestring
citystring
provincestring
countrystring (default: "Canada")
unitnumberstring (optional)
floorstring (optional)

Error handling

StatusErrorWhat to do
401X-Webhook-Secret header requiredAdd the header
401Invalid webhook secretCheck your secret matches what’s in the dashboard
403Plan version does not belong to your service providerWrong plan_version_id for your account
404Location not found for location_hashThe hash didn’t match any location record — subscriber may have used an expired link
422Invalid location_hash formatMust be exactly 10 hex characters
400Plan version not activeThe plan’s effective date window has passed
429Rate limit exceeded120 requests/minute per IP
On 5xx errors: Safe to retry the full request. kurnl rolls back any partial state automatically on failure. On network timeout: Retry the request — it is idempotent. The same email + plan_version_id combination returns the existing job_id rather than creating a duplicate.

Anonymous variant

If you own the end-user relationship and don’t want kurnl to store subscriber PII, use the anonymous variant instead. You provide an external_subscription_id (your own identifier) instead of a subscriber object. kurnl provisions the port but creates no subscriber record.
With the anonymous variant, subscribers have no kurnl self-service portal access and kurnl cannot issue invoices on your behalf.