API Documentation

Welcome to the {{BRAND_NAME}} API documentation. This API allows you to programmatically manage customers, services, billing, invoicing, and telecom operations.

This is the API documentation home. This page is the primary entry point for concepts, workflows and examples. For interactive endpoint exploration and live testing — and for the complete, always-current field list of every resource (the Schemas section) — use the Swagger UI, accessible via the sidebar link or the button below. The examples on this page are abbreviated; the Swagger schema is the authoritative field reference.

Base URL

{{BASE_URL}}

All requests use HTTPS. The API returns JSON by default.

Open Interactive API Explorer (Swagger UI)

Quick Start

To start using the API you need:

  1. A user account with "Is allowed to login to the API" enabled
  2. Your reseller's IP whitelist configured to include your application's IP
  3. Your client ID
Where do I find my client ID?
A client ID must be activated once by your platform provider. After activation, you can find it on your own customer page under Details as "API secret (client_id)". Contact your provider if you don't see it yet.
# 1. Get a token
curl -X POST {{BASE_URL}}/authentication-token \
  -H "Content-Type: application/json" \
  -d '{
    "email": "your-api-user@example.com",
    "password": "your-password",
    "client_id": "your-client-id"
  }'

# 2. Use the token
curl {{BASE_URL}}/customers \
  -H "Authorization: Bearer YOUR_TOKEN"
$client = new GuzzleHttp\Client([
    'base_uri' => '{{BASE_URL}}',
]);

// 1. Get a token
$response = $client->post('/authentication-token', [
    'json' => [
        'email'     => 'your-api-user@example.com',
        'password'  => 'your-password',
        'client_id' => 'your-client-id',
    ],
]);
$token = json_decode($response->getBody(), true)['token'];

// 2. Use the token
$response = $client->get('/customers', [
    'headers' => ['Authorization' => 'Bearer ' . $token],
]);
$customers = json_decode($response->getBody(), true);

Versioning & Changes

The API does not use URL-based versioning (no /v1/ or /v2/ prefix). New fields and endpoints may be added without notice — your integration should ignore unknown fields in responses to remain forward-compatible. Existing fields and endpoints are not removed or renamed without prior communication.

Documentation last updated: April 2026

Authentication

The API supports two authentication methods.

Option 1: JWT Token (recommended)

Request a token by posting your credentials. The token is then used in the Authorization header.

POST /authentication-token
curl -X POST {{BASE_URL}}/authentication-token \
  -H "Content-Type: application/json" \
  -d '{
    "email": "your-api-user@example.com",
    "password": "your-password",
    "client_id": "your-client-id"
  }'

# Response: { "token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9..." }

# Use in subsequent requests:
curl {{BASE_URL}}/customers \
  -H "Authorization: Bearer eyJ0eXAiOiJKV1Qi..."
use GuzzleHttp\Client;

$client = new Client(['base_uri' => '{{BASE_URL}}']);

$response = $client->post('/authentication-token', [
    'json' => [
        'email'     => 'your-api-user@example.com',
        'password'  => 'your-password',
        'client_id' => 'your-client-id',
    ],
]);

$token = json_decode($response->getBody(), true)['token'];

// All subsequent requests:
$response = $client->get('/customers', [
    'headers' => ['Authorization' => 'Bearer ' . $token],
]);

Option 2: Static API Key

Use CLIENT-ID and AUTH-TOKEN headers directly. Simpler but less flexible.

curl {{BASE_URL}}/customers \
  -H "CLIENT-ID: your-client-id" \
  -H "AUTH-TOKEN: your-auth-token"
$response = $client->get('/customers', [
    'headers' => [
        'CLIENT-ID'  => 'your-client-id',
        'AUTH-TOKEN'  => 'your-auth-token',
    ],
]);

Token Lifetime

JWT tokens expire after 1 hour. There is no refresh token endpoint — when a token expires, request a new one by posting credentials to /authentication-token again.

Production tip: Cache the token and only re-authenticate when you receive a 401 Unauthorized response. Do not request a new token before every API call.

Rate Limiting

The API uses a sliding window rate limiter with two simultaneous limits:

WindowHeadersDescription
1 hourX-RateLimit-1H-*Short-term burst protection
24 hoursX-RateLimit-24H-*Daily volume limit

When you exceed a limit, the API returns 429 Too Many Requests. Check the Retry-After header for when to resume.

Webhooks & Real-Time Updates

The API does not support webhooks or push notifications. To detect changes (e.g., service order status updates, new invoices), you need to poll the relevant endpoints at a suitable interval. Use filters like date ranges or status fields to limit the result set.

Pagination & Errors

Response Format (JSON-LD / Hydra)

The API uses JSON-LD with Hydra vocabulary. This means responses have a specific envelope format that differs from plain JSON APIs.

Example responses on this site are abbreviated. The JSON samples in these docs show the most relevant fields and end with ... — real responses contain more fields than shown. The complete, always-current field list per resource lives in the Schemas section of the Swagger UI, which is generated directly from the API and never drifts. Use this page for concepts and workflows; use the Swagger schema as the authoritative field reference.

Single resource

Each resource includes @id (its IRI — a path like /customers/1234) and @type:

GET /customers/1234
{
  "@context": "/contexts/Customer",
  "@id": "/customers/1234",
  "@type": "Customer",
  "id": 1234,
  "name": "Acme BV",
  "parent": "/customers/100",
  "customerType": "/customer-types/1",
  "reference": "ACM001",
  ...
}

Collection (list)

Collections are wrapped in a Hydra envelope. The actual items are in hydra:member:

GET /customers?parentId=100
{
  "@context": "/contexts/Customer",
  "@id": "/customers",
  "@type": "hydra:Collection",
  "hydra:totalItems": 42,
  "hydra:member": [
    { "@id": "/customers/1234", "@type": "Customer", "id": 1234, "name": "Acme BV", ... },
    { "@id": "/customers/1235", "@type": "Customer", "id": 1235, "name": "Beta Corp", ... }
  ],
  "hydra:view": {
    "@id": "/customers?parentId=100&page=1",
    "@type": "hydra:PartialCollectionView",
    "hydra:first": "/customers?parentId=100&page=1",
    "hydra:last": "/customers?parentId=100&page=2",
    "hydra:next": "/customers?parentId=100&page=2"
  }
}
IRI references: Related entities are referenced by their IRI path (e.g., "/customers/1234"), not by a numeric ID. When creating or updating resources, use IRI format: "parent": "/customers/100", not "parent": 100.

Pagination

Collection endpoints paginate responses. Default: 50 items, maximum: 500. Use hydra:view to navigate pages.

GET /customers?page=2&itemsPerPage=100
ParameterDescription
pagePage number (1-based)
itemsPerPageItems per page (default: 50, max: 500 for most resources)
Some resources like CDR Totals and Billing Rules support up to 50,000 items per page for large dataset exports. Use hydra:totalItems to know the total count and hydra:view.hydra:next to check if more pages exist.

Error Codes

CodeMeaning
200Success
201Resource created
400Bad request — check your parameters
401Unauthorized — token expired or invalid
403Forbidden — insufficient permissions or IP not whitelisted
404Resource not found
429Rate limit exceeded — wait and retry
500Server error
Example error response
{
  "type": "https://tools.ietf.org/html/rfc2616#section-10",
  "title": "An error occurred",
  "detail": "Access Denied.",
  "status": 403
}

Domain Overview

The {{BRAND_NAME}} API serves the Dutch MSP and telecom reseller market. Understanding the business model is essential for using the API effectively.

The Reseller Model

The platform is a multi-tenant system where resellers sell telecom and IT services to their end customers under their own brand.

Portal (top-level) └── Reseller A ├── End Customer 1 ├── End Customer 2 ├── Dealer X ← earns commission │ ├── End Customer 3 │ └── End Customer 4 └── Wholesaler Y ← gets invoiced for children ├── End Customer 5 └── End Customer 6

When authenticated, all data is scoped to the customer tree of the reseller that the API user belongs to.

Key Entities

EntityAPI ResourceWhat it represents
Customer/customersEnd customer, dealer, or wholesaler — hierarchically linked via parent
Order/ordersAn active service (internet, VoIP, mobile) — not a purchase order
Service Order/service-ordersA provisioning request to create/change/terminate a service
Billing Deal/billing-dealsA recurring subscription or one-time charge on an order
Billing Rule/billing-rulesIntermediate calculation during invoice generation
Invoice/invoicesFinal billing document sent to a customer
CDR Total/cdr-totalsAggregated call/data/SMS usage per service per month
User/usersPortal or API user belonging to a customer

How Entities Connect

Customer ├── Orders (services) │ ├── Billing Deals (subscriptions / one-time costs) │ │ └── Billing Rules (generated during invoice run) │ │ └── Invoice Lines → Invoice │ └── CDR Totals (usage per month) ├── Service Orders (provisioning → creates/modifies Orders) ├── Contracts └── Users

Customer Hierarchy

Customers are organized in a tree using parent references. All access control is based on this hierarchy.

Customer Types

TypeDescription
End CustomerRegular customer that uses services
DealerCommission partner — earns commission on end customer invoices
WholesalerGets consolidated invoices for all their end customers

Filtering by Hierarchy

ParameterWhat it doesExample
inTreeOfCustomerIdAll entities in the full subtree/customers?inTreeOfCustomerId=1234
customer.idDirectly linked to this customer/orders?customer.id=1234
includeTreeExpand customer.id to include subtree/orders?customer.id=1234&includeTree=1
parentIdDirect children only/customers?parentId=1234
Invoice redirection: A customer can have invoiceOnCustomerId set, meaning invoices go to a different customer. Use invoiceeId (not customerId) when querying invoices.

Billing Model

Billing Deal Types

typebillingTypeWhat it isExample
salesrecurringSubscription revenueVoIP trunk €49/mo
salesone-timeOne-time chargeSetup fee €150
purchaserecurringRecurring costWholesale cost from supplier
purchaseone-timeOne-time costHardware purchase
Important: rateEnduser is always a per-month rate, regardless of billing period. A quarterly-billed deal with rateEnduser: 50 generates a €150 invoice line each quarter.

Invoice Run Flow

Billing Deals (active, sales type) ↓ invoice run Billing Rules (one per charge line) ↓ approval Invoice Lines ↓ Invoice (concept → accepted → invoiced)

When a billing deal's dateEnd is before billedUntil, the system automatically creates a credit on the next invoice.

CDR Totals (Usage)

Understanding dateTo: this field contains the last day of the month (e.g., 2026-03-31 = all of March 2026). Each unique dateTo value represents one complete month.

When billingRuleId is set on a CDR total record, that usage has been invoiced.

Orders vs Service Orders

OrderService Order
What is it?An active serviceA provisioning request
AnalogyYour internet subscriptionThe activation ticket
LifecycleLong-lived (months/years)Short-lived (days/weeks)
Has billing?Yes — billing deals + CDR totalsNo
Endpoint/orders/service-orders
POST /service-orders → processing → GET /orders (new service exists)

To see what services a customer has, use GET /orders.
To request a new service, use POST /service-orders.

Known Issues & Quirks

The API has grown organically over time. Some naming conventions and parameter styles are inconsistent. This page documents the known inconsistencies so you don't waste time debugging them.

Customer ID Parameter Naming

The most common source of confusion: different endpoints use different parameter names for what is essentially the same thing — a customer ID.

ParameterUsed onNote
customer.id/orders, /service-ordersDot notation — refers to the related customer entity
customerId/users, /tariffplans, /products/.../pricescamelCase — direct ID parameter
customer_id/call-logssnake_case — legacy naming
invoiceeId/invoicesNot the customer who uses the service, but the one who receives the invoice
invoicerId/billing-dealsThe reseller/invoicer — required parameter
inTreeOfCustomerId/customers, /ordersHierarchy filter — includes all descendants
inTreeOfInvoiceeId/invoicesSame concept but for the invoicee hierarchy
contractee/contractsNo "Id" suffix — just the customer ID of the contractee
parentId/customersDirect parent only — not a tree filter
Tip: Always check the Swagger UI for the exact parameter name before building your integration. The parameter name is not guessable from convention alone.

Resource ID Formats

Most resources use /resource/{id} with a numeric ID in the URL path. However, some resources deviate:

PatternExampleNote
/resource/{id}/customers/1234Standard — most endpoints
?invoiceId=X/invoice-lines?invoiceId=9999Invoice lines require a query param, not a path
?invoicerId=X/billing-deals?invoicerId=1234Billing deals require invoicerId as query param
/resource/{id}/action/orders/5678/recurring-revenueSub-resource actions on an order

Pagination Differences

ResourceDefaultMaximum
Most resources50500
/cdr-totals5050,000
/billing-rules5050,000
/cdrs (CSV)50,00050,000 (fixed)
/raw-cdrs1,00020,000

Date Field Quirks

FieldResourceGotcha
dateTo/cdr-totalsContains the last day of the month, not a date range end. 2026-03-31 = March 2026.
billedUntil/billing-dealsMoves forward each invoice run. If dateEnd < billedUntil, a credit is generated.
rateEnduser/billing-dealsAlways a monthly rate, regardless of billing period (quarterly billing with rate 50 = €150/quarter).

Invoice vs Customer

A customer can have invoiceOnCustomerId set, redirecting invoices to a different customer (the "invoicee"). This means the customer using a service and the customer receiving the invoice can be different entities. When querying invoices, use invoiceeId — not customerId (which doesn't exist on invoices).

PUT vs PATCH vs POST

The API uses these methods inconsistently in some places:

MethodTypical UseQuirk
PUTFull replaceOn /customers and /billing-deals, PUT acts as a sync/upsert — it creates or updates based on a reference field
PATCHPartial updateUses application/merge-patch+json content type, not standard JSON
POSTCreate newStandard behavior

Content Types

The default content type for most requests is application/json, but there are exceptions:

EndpointContent Type
PATCH requestsapplication/merge-patch+json
/cdrsReturns text/csv
/invoices/{id}/get-pdfReturns application/pdf
/uploads/{id}/downloadReturns the original file's MIME type

Portal GUI ↔ API Field Mapping

The portal GUI is translated (Dutch/English) and column or field labels do not always match the API field names. This table maps the most-asked GUI labels to the API resource and field they come from.

GUI label (NL)GUI label (EN)Where in the portalAPI resourceField
AangaandeApplies to Column in the subscription lists (customer page → invoicing tab: recurring / one-time / inactive; service page) /billing-deals computedRelatedDescription — read-only; derived from the linked entity (e.g. phone number, service label) unless overridden
Aangaande / extra omschrijvingRelated description Field on the billing-deal edit form; manual override of the column above. Not shown on invoices. /billing-deals relatedDescription (writable)
Omschrijving op factuurInvoice line label Field on the billing-deal form; shown as the "Omschrijving" / "Description" column in subscription lists and on invoice lines /billing-deals, /invoice-lines invoiceLineLabel
Gefactureerd (t/m)Billed (until) Column in the subscription lists; billing-deal detail page /billing-deals billedUntil
Uw referentieYour reference Invoice page; the customer/PO reference printed on the invoice /invoices yourReference
BetaalstatusPayment state Invoice lists; customer page → invoices tab /invoices paymentStatus (paid, partial, unpaid)
Terminology: what the portal calls a subscription ("abonnement") is a BillingDeal in the API, and a service ("dienst") is an Order. See Known Issues & Quirks for more naming history.
Label not listed here? Don't guess a field by name similarity — check the Schemas section of the Swagger UI for the complete field list per resource, or ask support to confirm the mapping (we'll add it to this table).

Workflow: Monthly Invoice Sync

The most common integration scenario: periodically fetch new invoices and sync them to your accounting system, data warehouse, or client portal.

Overview

The flow consists of three steps: authenticate, fetch invoices for the target period, then retrieve the line-level detail for each invoice. You can optionally download the invoice PDF.

Polling strategy: Since the API does not support webhooks, run this workflow on a schedule (e.g., daily or after each invoice run). Use invoiceDateFrom / invoiceDateTo filters to fetch only the new period.

Step 1 — Fetch invoices for a date range

Use GET /invoices with date filters. Add exclude[]=invoiceLines to keep the response lightweight when you only need the invoice headers first.

# Fetch invoices for March 2026
curl "{{BASE_URL}}/invoices?invoiceDateFrom=2026-03-01&invoiceDateTo=2026-03-31&exclude[]=invoiceLines&exclude[]=invoiceUploads" \
  -H "Authorization: Bearer $TOKEN"
// Fetch invoices for March 2026
$response = $client->get('/invoices', [
    'headers' => ['Authorization' => 'Bearer ' . $token],
    'query'   => [
        'invoiceDateFrom' => '2026-03-01',
        'invoiceDateTo'   => '2026-03-31',
        'exclude'         => ['invoiceLines', 'invoiceUploads'],
    ],
]);
$invoices = json_decode($response->getBody(), true)['hydra:member'];

Step 2 — Get line-level detail per invoice

For each invoice, fetch the individual lines using GET /invoice-lines. Invoice lines contain the description, amounts, VAT, and product references.

# Get lines for invoice 9999
curl "{{BASE_URL}}/invoice-lines?invoiceId=9999" \
  -H "Authorization: Bearer $TOKEN"
foreach ($invoices as $invoice) {
    $invoiceId = $invoice['id'];

    $response = $client->get('/invoice-lines', [
        'headers' => ['Authorization' => 'Bearer ' . $token],
        'query'   => ['invoiceId' => $invoiceId],
    ]);
    $lines = json_decode($response->getBody(), true)['hydra:member'];

    // Process each line (description, amount, VAT, product, etc.)
    foreach ($lines as $line) {
        // Sync to your accounting system...
    }
}
Alternative: Billing Rules
If you need more granular detail than invoice lines provide — for example, the underlying billing deal, calculation period, or per-unit breakdown — use GET /billing-rules?invoiceId={id} instead of (or in addition to) invoice lines. Billing rules are the intermediate calculation records that explain how each line was computed. They support high pagination (up to 50,000 items per request).

Step 3 (optional) — Download the PDF

If you need the rendered invoice document, download the PDF for each invoice.

curl "{{BASE_URL}}/invoices/9999/get-pdf" \
  -H "Authorization: Bearer $TOKEN" \
  -o invoice_9999.pdf
foreach ($invoices as $invoice) {
    $invoiceId = $invoice['id'];

    $pdf = $client->get("/invoices/{$invoiceId}/get-pdf", [
        'headers' => ['Authorization' => 'Bearer ' . $token],
    ]);
    file_put_contents("invoices/invoice_{$invoiceId}.pdf", $pdf->getBody());
}

Tips

TopicRecommendation
IdempotencyStore the invoice id in your system and skip duplicates on re-runs.
HierarchyUse inTreeOfInvoiceeId to fetch invoices for an entire customer tree in one call.
Excel exportUse /invoices/{id}/excel-specification for a detailed Excel breakdown per invoice.
Token expiryTokens expire after 1 hour. For large syncs, re-authenticate when you receive a 401 response.
PaginationInvoice lists are paginated. Loop through hydra:viewhydra:next until there are no more pages.

Customers

GET/customers
POST/customers
PUT/customers (sync — create or update)
GET/customers/{id}
PATCH/customers/{id}

List customers

Query Parameters

ParameterTypeDescription
namestringSearch by name (partial match)
referencestringFilter by reference code
debtorNumberstringFilter by debtor number
invoiceEmailstringFilter by invoice email
inTreeOfCustomerIdintegerAll customers within this customer's hierarchy
parentIdintegerDirect children of this customer
phonenumberAnywherestringSearch by phone number across services and users
curl "{{BASE_URL}}/customers?inTreeOfCustomerId=1234" \
  -H "Authorization: Bearer $TOKEN"
$response = $client->get('/customers', [
    'headers' => ['Authorization' => 'Bearer ' . $token],
    'query'   => ['inTreeOfCustomerId' => 1234],
]);
$customers = json_decode($response->getBody(), true);

Create a customer

curl -X POST "{{BASE_URL}}/customers" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "New Customer BV",
    "parent": "/customers/1234",
    "customerType": "/customer-types/1",
    "reference": "NC001",
    "invoiceEmail": "invoices@example.nl"
  }'
$response = $client->post('/customers', [
    'headers' => ['Authorization' => 'Bearer ' . $token],
    'json'    => [
        'name'         => 'New Customer BV',
        'parent'       => '/customers/1234',
        'customerType' => '/customer-types/1',
        'reference'    => 'NC001',
        'invoiceEmail' => 'invoices@example.nl',
    ],
]);
Use IRI format for references: /customers/1234, not just 1234.

Example response

GET /customers/1234
{
  "@id": "/customers/1234",
  "@type": "Customer",
  "id": 1234,
  "name": "Acme Telecom BV",
  "reference": "ACM001",
  "debtorNumber": "D10042",
  "invoiceEmail": "facturen@acme-telecom.nl",
  "parent": "/customers/100",
  "customerType": "/customer-types/1",
  "accountManager": "/users/56",
  "isActive": true,
  "invoiceOnCustomerId": null,
  "visitLocation": { "street": "Keizersgracht", "houseNumber": "100", "zipcode": "1015AA", "city": "Amsterdam" },
  "paymentMethod": "direct_debit",
  "paymentTerm": 30,
  "vatReversedCharge": false,
  ...
}

↳ Trimmed for brevity — see the Swagger UI (Schemas) for the complete, always-current field list.

Common errors when creating/updating customers

ErrorCauseFix
400 "parent: This value should not be blank"Missing parent referenceInclude "parent": "/customers/{id}" in IRI format
400 "customerType: This value should not be null"Missing customer typeInclude "customerType": "/customer-types/{id}"
403 Access DeniedParent customer not in your treeVerify the parent ID is within your reseller's hierarchy
400 "reference: This value is already used"Duplicate reference codeUse a unique reference or use PUT to sync/upsert

Orders (Services)

GET/orders
POST/orders
GET/orders/{id}
PATCH/orders/{id}
GET/orders/{id}/recurring-revenue
GET/orders/{id}/activate-billing-deals
GET/orders/{id}/free-radius-status/{pppUsername}
GET/orders/kpn-wba-line-check-details/{orderId}/{serviceInstanceId}

Get services for a customer

Query Parameters

ParameterTypeDescription
customer.idintegerFilter by customer
includeTreeintegerSet to 1 to include child customers' orders
inTreeOfCustomerIdintegerAll orders in this customer's hierarchy
supplier.idintegerFilter by supplier
serviceType.idintegerFilter by service type
curl "{{BASE_URL}}/orders?customer.id=1234&includeTree=1" \
  -H "Authorization: Bearer $TOKEN"
$response = $client->get('/orders', [
    'headers' => ['Authorization' => 'Bearer ' . $token],
    'query'   => ['customer.id' => 1234, 'includeTree' => 1],
]);
$orders = json_decode($response->getBody(), true);

Get monthly recurring revenue

curl "{{BASE_URL}}/orders/5678/recurring-revenue" \
  -H "Authorization: Bearer $TOKEN"

# Response: { "activeRevenue": 149.95, "pendingRevenue": 25.00 }
$response = $client->get('/orders/5678/recurring-revenue', [
    'headers' => ['Authorization' => 'Bearer ' . $token],
]);
$revenue = json_decode($response->getBody(), true);
// $revenue['activeRevenue'], $revenue['pendingRevenue']

Example response

GET /orders/5678
{
  "@id": "/orders/5678",
  "@type": "Order",
  "id": 5678,
  "customer": "/customers/1234",
  "serviceType": "/service-types/3",
  "supplier": "/suppliers/10",
  "label": "Internet 1Gbps Fiber",
  "description": "Glasvezel aansluiting Keizersgracht 100",
  "dateStart": "2024-01-15",
  "dateEnd": null,
  "isActive": true,
  "costCenter": "HQ-NET",
  "contract": "/contracts/89",
  "location": { "street": "Keizersgracht", "houseNumber": "100", ... },
  "orderDetailsGeneric": "/order-details-generics/5678",
  "parentId": null,
  "children": [],
  ...
}

↳ Trimmed for brevity — see the Swagger UI (Schemas) for the complete, always-current field list.

The orderDetailsGeneric field points to the typed detail sub-resource. Depending on the service type, there may also be an orderDetailsX2voip, orderDetailsConnection, or another typed detail IRI.

Grouped services (parent-child)

Services can be grouped in a 1-level parent-child hierarchy. Every order response includes two read-only fields:

FieldTypeParent serviceChild serviceStandalone service
parentIdinteger / nullnullID of the parent ordernull
childrenarray of {id}Non-empty — contains child IDs[][]
GET /orders/1200 — parent service
{
  "id": 1200,
  "description": "Bundled internet + VoIP",
  "parentId": null,
  "children": [
    { "id": 1201 },
    { "id": 1202 }
  ],
  ...
}

↳ Trimmed for brevity — see the Swagger UI (Schemas) for the complete, always-current field list.

GET /orders/1201 — child service
{
  "id": 1201,
  "description": "VoIP add-on",
  "parentId": 1200,
  "children": [],
  ...
}

↳ Trimmed for brevity — see the Swagger UI (Schemas) for the complete, always-current field list.

Grouping is read-only via the API — it is managed in the portal. A service is either a parent or a child, never both.

Billing Deals

GET/billing-deals
POST/billing-deals
GET/billing-deals/{id}
PATCH/billing-deals/{id}
PUT/billing-deals/{id}

Get active subscriptions

Query Parameters

ParameterTypeDescription
invoicerId requiredintegerThe reseller/invoicer customer ID
invoiceeIdintegerThe customer being invoiced
orderIdintegerFilter by order (service)
typestringsales or purchase
onlyActivebooleanOnly return active deals
costCenterstringFilter by cost center
relatedDescriptionstringFilter by related description (substring match)
# invoicerId is required
curl "{{BASE_URL}}/billing-deals?invoicerId=1234&onlyActive=true&type=sales" \
  -H "Authorization: Bearer $TOKEN"
$deals = $client->get('/billing-deals', [
    'headers' => ['Authorization' => 'Bearer ' . $token],
    'query'   => [
        'invoicerId' => 1234,
        'onlyActive' => true,
        'type'       => 'sales',
    ],
]);

// Sum rateEnduser for monthly recurring revenue
$data = json_decode($deals->getBody(), true);
$mrr = array_sum(array_column($data, 'rateEnduser'));

Example response

GET /billing-deals/9001
{
  "@id": "/billing-deals/9001",
  "@type": "BillingDeal",
  "id": 9001,
  "type": "sales",
  "billingType": "recurring",
  "orderId": 5678,
  "invoicerId": 100,
  "invoiceeId": 1234,
  "product": "/products/42",
  "billingDealPeriod": "/billing-deal-periods/1",
  "rateEnduser": 49.95,
  "ratePurchase": 32.50,
  "dateStart": "2024-01-15",
  "dateEnd": null,
  "billedUntil": "2026-04-30",
  "invoiceLineLabel": "Internet 1Gbps Fiber",
  "costCenter": "HQ-NET",
  "computedRelatedDescription": "Internet 1Gbps Fiber (KPN EZT-12345)",
  "contract": "/contracts/89",
  ...
}

↳ Trimmed for brevity — see the Swagger UI (Schemas) for the complete, always-current field list.

"Aangaande" / "Applies to": the Aangaande column shown in the portal's subscription lists (customer page → invoicing tab, service page) is computedRelatedDescription. It is read-only and derived from the entity the deal is linked to (e.g. a phone number or service label), unless a manual override is set via relatedDescription — the "Aangaande / extra omschrijving" field on the billing-deal edit form. The override is writable through POST/PATCH and is not shown on invoices, only in the portal GUI. See also Portal GUI ↔ API field mapping.
Remember: rateEnduser and ratePurchase are always monthly rates. A quarterly billing deal with rateEnduser: 49.95 generates a €149.85 invoice line per quarter.

Invoices

GET/invoices
GET/invoices/{id}
PATCH/invoices/{id}
GET/invoices/{id}/get-pdf
GET/invoices/{id}/excel-specification

Get unpaid invoices

Query Parameters

ParameterTypeDescription
invoiceeIdintegerThe customer who receives the invoice
inTreeOfInvoiceeIdintegerAll invoices in a customer hierarchy
invoiceDateFromstringStart date (YYYY-MM-DD)
invoiceDateTostringEnd date (YYYY-MM-DD)
paymentStatusstringpaid, partial, or unpaid
typestringend-user, commission, or reseller
excludearrayExclude: invoiceLines, invoiceUploads
curl "{{BASE_URL}}/invoices?invoiceeId=1234&paymentStatus=unpaid" \
  -H "Authorization: Bearer $TOKEN"

# Download PDF:
curl "{{BASE_URL}}/invoices/9999/get-pdf" \
  -H "Authorization: Bearer $TOKEN" \
  -o invoice_9999.pdf
$invoices = $client->get('/invoices', [
    'headers' => ['Authorization' => 'Bearer ' . $token],
    'query'   => [
        'invoiceeId'    => 1234,
        'paymentStatus' => 'unpaid',
    ],
]);

// Download PDF:
$pdf = $client->get("/invoices/9999/get-pdf", [
    'headers' => ['Authorization' => 'Bearer ' . $token],
]);
file_put_contents('invoice.pdf', $pdf->getBody());

Example response

GET /invoices/9999
{
  "@id": "/invoices/9999",
  "@type": "Invoice",
  "id": 9999,
  "invoicerId": 100,
  "invoiceeId": 1234,
  "invoiceNumber": "INV-2026-0042",
  "invoiceDate": "2026-04-01",
  "type": "end-user",
  "state": "invoiced",
  "paymentStatus": "unpaid",
  "amountExclVat": 149.85,
  "amountVat": 31.47,
  "amountInclVat": 181.32,
  "amountPaid": 0,
  "yourReference": "PO-2026-100",
  "invoiceLines": [
    { "@id": "/invoice-lines/50001", "description": "Internet 1Gbps Fiber (jan-mrt)", "amountExclVat": 149.85, ... }
  ],
  ...
}

↳ Trimmed for brevity — see the Swagger UI (Schemas) for the complete, always-current field list.

Invoice Lines & Billing Rules

GET/invoice-lines?invoiceId={id}
GET/billing-rules?invoiceId={id}

Both require invoiceId as parameter. Billing rules are read-only.

CDR Totals (Usage Data)

GET/cdr-totals
GET/cdr-totals/{id}
Understanding dateTo: This is the last day of the month (e.g., 2026-03-31 = all of March 2026). It represents the entire month, not a single day.

Response Fields

PropertyTypeDescription
orderIdintegerThe service this usage belongs to
clistringPhone number (Calling Line ID)
dateTostringLast day of month (= month identifier)
callsintegerNumber of calls
durationintegerTotal seconds
cost1 / cost2 / cost3floatCost tiers
billingRuleIdinteger/nullIf set → usage has been invoiced

Supports high pagination — up to 50,000 items per page.

curl "{{BASE_URL}}/cdr-totals?itemsPerPage=500" \
  -H "Authorization: Bearer $TOKEN"
$cdrTotals = $client->get('/cdr-totals', [
    'headers' => ['Authorization' => 'Bearer ' . $token],
    'query'   => ['itemsPerPage' => 500],
]);
$data = json_decode($cdrTotals->getBody(), true);

// Find unbilled usage:
$unbilled = array_filter($data, fn($c) => $c['billingRuleId'] === null);

Service Orders

Provisioning requests to create, modify, or terminate services.

GET/service-orders
POST/service-orders
GET/service-orders/{id}
POST/service-orders/{id}/perform-porting-task
POST/service-orders/{id}/cancel

Service orders have typed detail objects depending on the service type. Check the Swagger UI for the full list of POST examples per service type.

Check service order status

# Get service orders for a customer
curl "{{BASE_URL}}/service-orders?customer.id=1234" \
  -H "Authorization: Bearer $TOKEN"

# Cancel a pending service order
curl -X POST "{{BASE_URL}}/service-orders/7890/cancel" \
  -H "Authorization: Bearer $TOKEN"
$response = $client->get('/service-orders', [
    'headers' => ['Authorization' => 'Bearer ' . $token],
    'query'   => ['customer.id' => 1234],
]);
$orders = json_decode($response->getBody(), true);

// Cancel a pending service order
$client->post('/service-orders/7890/cancel', [
    'headers' => ['Authorization' => 'Bearer ' . $token],
]);

Common errors when creating service orders

ErrorCauseFix
400 validation errorsMissing or invalid fields in the request bodyEach service type requires different fields — check Swagger UI examples for the exact payload per type
403 Access DeniedCustomer not in your tree, or service orders not enabledVerify customer access and that your reseller has service order permissions
400 "customer: This value should not be null"Missing customer IRIInclude "customer": "/customers/{id}"

Service Order Tasks

Tasks associated with service orders. Each service order can have one or more tasks that track the individual steps required to complete the order (e.g., porting tasks, provisioning steps).

GET/service-order-tasks
GET/service-order-tasks/{id}

List tasks for a service order

curl "{{BASE_URL}}/service-order-tasks?serviceOrder=/service-orders/123" \
  -H "Authorization: Bearer $TOKEN"
$response = $client->get('/service-order-tasks', [
    'headers' => ['Authorization' => 'Bearer ' . $token],
    'query'   => ['serviceOrder' => '/service-orders/123'],
]);
$tasks = json_decode($response->getBody(), true);

Service Order Products

Products attached to a service order. These represent the specific products that are being ordered, changed, or terminated as part of a service order.

GET/service-order-products/{id}

Get product details for a service order

curl "{{BASE_URL}}/service-order-products/456" \
  -H "Authorization: Bearer $TOKEN"
$response = $client->get('/service-order-products/456', [
    'headers' => ['Authorization' => 'Bearer ' . $token],
]);
$product = json_decode($response->getBody(), true);

Users

GET/users
POST/users
GET/users/{id}
PATCH/users/{id}

List users

Query Parameters

ParameterTypeDescription
customerIdintegerFilter by customer
emailstringFilter by email address
curl "{{BASE_URL}}/users?customerId=1234" \
  -H "Authorization: Bearer $TOKEN"
$response = $client->get('/users', [
    'headers' => ['Authorization' => 'Bearer ' . $token],
    'query'   => ['customerId' => 1234],
]);
$users = json_decode($response->getBody(), true);
A user needs allowApiLogin = true to authenticate via the API. The IP whitelist is enforced unless bypassApiWhitelist = true.

Contracts

Contracts define commercial terms between a contractor (reseller) and a contractee (customer). They can contain billing deal periods, cost centers, and date ranges.

GET/contracts
POST/contracts
GET/contracts/{id}
PATCH/contracts/{id}
PUT/contracts/{id}

Get contracts for a customer

Query Parameters

ParameterTypeDescription
contracteeintegerFilter contracts by contractee (customer) ID
curl "{{BASE_URL}}/contracts?contractee=1234" \
  -H "Authorization: Bearer $TOKEN"
$response = $client->get('/contracts', [
    'headers' => ['Authorization' => 'Bearer ' . $token],
    'query'   => ['contractee' => 1234],
]);
$contracts = json_decode($response->getBody(), true);

Contract Purchases

Contract purchases link purchase billing deals to contracts. They represent the cost side of a contract, tracking what the reseller pays the supplier for a specific contract.

GET/contract-purchases
POST/contract-purchases
GET/contract-purchases/{id}
PUT/contract-purchases/{id}
PATCH/contract-purchases/{id}

Get purchases for a contract

curl "{{BASE_URL}}/contract-purchases?contract=/contracts/123" \
  -H "Authorization: Bearer $TOKEN"
$response = $client->get('/contract-purchases', [
    'headers' => ['Authorization' => 'Bearer ' . $token],
    'query'   => ['contract' => '/contracts/123'],
]);
$purchases = json_decode($response->getBody(), true);

Groups

Groups allow organizing customers into logical collections. A group can be used for reporting, filtering, or applying shared configuration across multiple customers.

GET/groups
GET/groups/{id}

List groups

curl "{{BASE_URL}}/groups" \
  -H "Authorization: Bearer $TOKEN"
$response = $client->get('/groups', [
    'headers' => ['Authorization' => 'Bearer ' . $token],
]);
$groups = json_decode($response->getBody(), true);

Invoice Lines

Invoice lines are the individual charges on an invoice, generated from billing rules during the invoice run. This is a read-only resource.

GET/invoice-lines

Get lines for an invoice

Query Parameters

ParameterTypeDescription
invoiceId requiredintegerThe invoice ID to retrieve lines for
curl "{{BASE_URL}}/invoice-lines?invoiceId=9999" \
  -H "Authorization: Bearer $TOKEN"
$response = $client->get('/invoice-lines', [
    'headers' => ['Authorization' => 'Bearer ' . $token],
    'query'   => ['invoiceId' => 9999],
]);
$lines = json_decode($response->getBody(), true);

Billing Rules

Billing rules are intermediate calculation records generated during the invoice run. They determine how billing deals translate into invoice lines. This is a read-only resource with high pagination support (up to 50,000 items).

GET/billing-rules
Billing rules can have the following states: accepted, forced, waiting, failed, skipped, ignored, processed.

Get billing rules for an invoice

Query Parameters

ParameterTypeDescription
invoiceIdintegerFilter by invoice
curl "{{BASE_URL}}/billing-rules?invoiceId=9999&itemsPerPage=500" \
  -H "Authorization: Bearer $TOKEN"
$response = $client->get('/billing-rules', [
    'headers' => ['Authorization' => 'Bearer ' . $token],
    'query'   => ['invoiceId' => 9999, 'itemsPerPage' => 500],
]);
$rules = json_decode($response->getBody(), true);

CDRs (CSV Export)

Export raw Call Detail Records as a CSV file. This endpoint returns CSV data rather than JSON, suitable for bulk data processing. Pagination is fixed at 50,000 records.

GET/cdrs
This endpoint returns text/csv format, not JSON. Use appropriate accept headers or handle the response as plain text.

Export CDR records

curl "{{BASE_URL}}/cdrs" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Accept: text/csv" \
  -o cdrs_export.csv
$response = $client->get('/cdrs', [
    'headers' => [
        'Authorization' => 'Bearer ' . $token,
        'Accept'        => 'text/csv',
    ],
]);
file_put_contents('cdrs_export.csv', $response->getBody());

Reports (CSV Export)

Run the portal's custom reports. First list the reports you are allowed to run, then download any of them as a CSV file. Which reports you see and may download is governed entirely by the portal — your reseller tree, your role/audience, per-report user-group restrictions, and a per-report API toggle. The API enforces no rules of its own; a report you are not entitled to simply does not appear in the list and returns 403 Forbidden on download.

List available reports

Returns the reports available to you. Each entry includes its id, name, description, category and the column headers the CSV will contain.

GET/reports
curl "{{BASE_URL}}/reports" \
  -H "Authorization: Bearer $TOKEN"
$response = $client->get('/reports', [
    'headers' => ['Authorization' => 'Bearer ' . $token],
]);
$reports = json_decode($response->getBody(), true);

Example response:

[
  {
    "id": 42,
    "name": "Monthly revenue per customer",
    "description": "Recurring revenue grouped by customer",
    "category": "Finance",
    "headers": ["customer", "reference", "revenue"],
    "csv_only": false
  }
  ...
]

Download a report as CSV

Streams the chosen report as a CSV file. Values use a semicolon (;) column separator and a point (.) as the decimal separator. Pass the id from the list endpoint.

GET/reports/{id}/export-csv
This endpoint returns text/csv format, not JSON. Use appropriate accept headers or handle the response as plain text. If you are not allowed to run the report (or it is not exposed to the API), it responds with 403 Forbidden and a JSON error body.
curl "{{BASE_URL}}/reports/42/export-csv" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Accept: text/csv" \
  -o report.csv
$response = $client->get('/reports/42/export-csv', [
    'headers' => [
        'Authorization' => 'Bearer ' . $token,
        'Accept'        => 'text/csv',
    ],
]);
file_put_contents('report.csv', $response->getBody());

Raw CDRs

Access raw CDR/XDR data with detailed call-level information. Default pagination is 1,000 records, maximum 20,000. Requires superuser or wholesaler privileges.

GET/raw-cdrs
This endpoint requires superuser or wholesaler role.

Get raw CDR data

curl "{{BASE_URL}}/raw-cdrs?itemsPerPage=5000" \
  -H "Authorization: Bearer $TOKEN"
$response = $client->get('/raw-cdrs', [
    'headers' => ['Authorization' => 'Bearer ' . $token],
    'query'   => ['itemsPerPage' => 5000],
]);
$rawCdrs = json_decode($response->getBody(), true);

Products

Products represent items from the product catalog that can be attached to billing deals. You can also look up pricing per customer and query KPN products available at a specific address.

GET/products
POST/products
GET/products/{id}
PATCH/products/{id}
PUT/products/{id}
GET/products/{id}/prices/{customerId}
GET/products/getKpnProducts/{zipcode}/{housenumber}/{customerId}

Get product pricing for a customer

# Get price for product 42, customer 1234
curl "{{BASE_URL}}/products/42/prices/1234" \
  -H "Authorization: Bearer $TOKEN"

# Response: { "finalPrice": 24.95, "isCustomerPrice": true }
$response = $client->get('/products/42/prices/1234', [
    'headers' => ['Authorization' => 'Bearer ' . $token],
]);
$pricing = json_decode($response->getBody(), true);
// $pricing['finalPrice'], $pricing['isCustomerPrice']

Check KPN products at an address

curl "{{BASE_URL}}/products/getKpnProducts/1234AB/10/1234?houseNumberExtension=A" \
  -H "Authorization: Bearer $TOKEN"
$response = $client->get('/products/getKpnProducts/1234AB/10/1234', [
    'headers' => ['Authorization' => 'Bearer ' . $token],
    'query'   => ['houseNumberExtension' => 'A'],
]);
$kpnProducts = json_decode($response->getBody(), true);

Tariff Plans

Tariff plans define pricing structures that can be assigned to customers. This is a read-only resource.

GET/tariffplans
GET/tariffplans/{id}

List tariff plans

Query Parameters

ParameterTypeDescription
customerIdintegerGet tariff plans managed by this customer
curl "{{BASE_URL}}/tariffplans?customerId=1234" \
  -H "Authorization: Bearer $TOKEN"
$response = $client->get('/tariffplans', [
    'headers' => ['Authorization' => 'Bearer ' . $token],
    'query'   => ['customerId' => 1234],
]);
$plans = json_decode($response->getBody(), true);

Product Categories

Product categories group products at a high level (e.g., "Mobile", "Fixed Line", "Hardware"). Categories contain product-category-products that link individual products.

GET/product-categories
GET/product-categories/{id}
GET/product-category-products
POST/product-category-products
GET/product-category-products/{id}
DELETE/product-category-products/{id}

List product categories

curl "{{BASE_URL}}/product-categories" \
  -H "Authorization: Bearer $TOKEN"
$response = $client->get('/product-categories', [
    'headers' => ['Authorization' => 'Bearer ' . $token],
]);
$categories = json_decode($response->getBody(), true);

Product Groups

Product groups organize products for accounting and reporting purposes. They determine which ledger accounts are used when exporting billing deals to accounting software.

GET/product-groups
GET/product-groups/{id}

List product groups

curl "{{BASE_URL}}/product-groups" \
  -H "Authorization: Bearer $TOKEN"
$response = $client->get('/product-groups', [
    'headers' => ['Authorization' => 'Bearer ' . $token],
]);
$groups = json_decode($response->getBody(), true);

Product Plans

Product plans define sets of products with specific pricing, assigned to customers via product-plan-invoicees. Products within a plan are managed via product-plan-products.

GET/product-plans
POST/product-plans
GET/product-plans/{id}
PUT/product-plans/{id}
PATCH/product-plans/{id}
DELETE/product-plans/{id}

Product Plan Invoicees

Link product plans to specific customers (invoicees):

GET/product-plan-invoicees
POST/product-plan-invoicees
GET/product-plan-invoicees/{id}
DELETE/product-plan-invoicees/{id}

Product Plan Products

Manage products within a plan, including plan-specific pricing:

GET/product-plan-products
POST/product-plan-products
GET/product-plan-products/{id}
PUT/product-plan-products/{id}
PATCH/product-plan-products/{id}
DELETE/product-plan-products/{id}

Get a product plan with its products

# List product plans
curl "{{BASE_URL}}/product-plans" \
  -H "Authorization: Bearer $TOKEN"

# Get products in a plan
curl "{{BASE_URL}}/product-plan-products?productPlan=/product-plans/42" \
  -H "Authorization: Bearer $TOKEN"
$response = $client->get('/product-plans', [
    'headers' => ['Authorization' => 'Bearer ' . $token],
]);
$plans = json_decode($response->getBody(), true);

// Get products in a specific plan
$response = $client->get('/product-plan-products', [
    'headers' => ['Authorization' => 'Bearer ' . $token],
    'query'   => ['productPlan' => '/product-plans/42'],
]);
$planProducts = json_decode($response->getBody(), true);

Phone Numbers

Manage phone numbers linked to services (orders). You can filter by order, update cost center and description fields.

GET/phonenumbers
GET/phonenumbers/{id}
PATCH/phonenumbers/{id}

Get phone numbers for a service

Query Parameters

ParameterTypeDescription
orderIdintegerGet all phone numbers from this order (service)
blockStartintegerGet phone numbers starting with this number
excludeOrdersintegerSet to 1 to exclude order data (faster response)
curl "{{BASE_URL}}/phonenumbers?orderId=5678" \
  -H "Authorization: Bearer $TOKEN"
$response = $client->get('/phonenumbers', [
    'headers' => ['Authorization' => 'Bearer ' . $token],
    'query'   => ['orderId' => 5678],
]);
$numbers = json_decode($response->getBody(), true);

Update cost center on a phone number

curl -X PATCH "{{BASE_URL}}/phonenumbers/123" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/merge-patch+json" \
  -d '{"costCenter": "Sales", "description": "Reception line"}'
$response = $client->patch('/phonenumbers/123', [
    'headers' => [
        'Authorization' => 'Bearer ' . $token,
        'Content-Type'  => 'application/merge-patch+json',
    ],
    'json' => [
        'costCenter'  => 'Sales',
        'description' => 'Reception line',
    ],
]);

SIM Cards

Manage SIM cards (including eSIMs) linked to mobile services. SIM cards contain ICC and IMSI identifiers and are linked to orders.

GET/simcards
POST/simcards
GET/simcards/{id}
PATCH/simcards/{id}

List SIM cards

curl "{{BASE_URL}}/simcards" \
  -H "Authorization: Bearer $TOKEN"
$response = $client->get('/simcards', [
    'headers' => ['Authorization' => 'Bearer ' . $token],
]);
$simcards = json_decode($response->getBody(), true);

Number Blocks

Manage VoIP number blocks. Supports splitting blocks and managing cool-off periods. Requires superuser or wholesaler privileges.

GET/number-blocks
GET/number-blocks/{id}
GET/number-blocks/{id}/end-cool-off
GET/number-blocks/{id}/split/{newBlockSize}

List number blocks

Query Parameters

ParameterTypeDescription
excludedNumberPoolIds[]arrayExclude specific number pool IDs
curl "{{BASE_URL}}/number-blocks" \
  -H "Authorization: Bearer $TOKEN"
$response = $client->get('/number-blocks', [
    'headers' => ['Authorization' => 'Bearer ' . $token],
]);
$blocks = json_decode($response->getBody(), true)['hydra:member'];

Split a number block

# Split block 456 into blocks of size 10
curl "{{BASE_URL}}/number-blocks/456/split/10" \
  -H "Authorization: Bearer $TOKEN"

# End cool-off period for a number block
curl "{{BASE_URL}}/number-blocks/456/end-cool-off" \
  -H "Authorization: Bearer $TOKEN"
// Split block 456 into blocks of size 10
$response = $client->get('/number-blocks/456/split/10', [
    'headers' => ['Authorization' => 'Bearer ' . $token],
]);

// End cool-off period
$response = $client->get('/number-blocks/456/end-cool-off', [
    'headers' => ['Authorization' => 'Bearer ' . $token],
]);

Area Codes

Read-only reference data for Dutch telephone area codes.

GET/areacodes
GET/areacodes/{id}

List area codes

curl "{{BASE_URL}}/areacodes" \
  -H "Authorization: Bearer $TOKEN"
$response = $client->get('/areacodes', [
    'headers' => ['Authorization' => 'Bearer ' . $token],
]);
$areacodes = json_decode($response->getBody(), true);

Tracking Devices

Manage GPS tracking devices linked to services. Devices have unit codes and can be linked to SIM cards and orders.

GET/tracking-devices
POST/tracking-devices
GET/tracking-devices/{id}
PATCH/tracking-devices/{id}

List tracking devices

Query Parameters

ParameterTypeDescription
unit_code_filterstringFilter by unit code
curl "{{BASE_URL}}/tracking-devices" \
  -H "Authorization: Bearer $TOKEN"
$response = $client->get('/tracking-devices', [
    'headers' => ['Authorization' => 'Bearer ' . $token],
]);
$devices = json_decode($response->getBody(), true);

Dashboard (MRR)

Retrieve Monthly Recurring Revenue (MRR) metrics and work-in-progress data for your reseller, specific users, or individual customers.

GET/dashboard/wip
GET/dashboard/user/{id}
GET/dashboard/customer/{id}/{filterField}

Endpoints

PathDescription
/dashboard/wipGet WIP one-time and recurring revenue for the reseller
/dashboard/user/{id}Get MRR metrics for a specific user (account manager)
/dashboard/customer/{id}/{filterField}Get MRR for a customer. filterField can be id or zendesk_sell_id

Get work-in-progress MRR

# Reseller-wide WIP metrics
curl "{{BASE_URL}}/dashboard/wip" \
  -H "Authorization: Bearer $TOKEN"

# MRR for a specific customer
curl "{{BASE_URL}}/dashboard/customer/1234/id" \
  -H "Authorization: Bearer $TOKEN"

# MRR by Zendesk Sell ID
curl "{{BASE_URL}}/dashboard/customer/99887766/zendesk_sell_id" \
  -H "Authorization: Bearer $TOKEN"
// Reseller-wide WIP
$response = $client->get('/dashboard/wip', [
    'headers' => ['Authorization' => 'Bearer ' . $token],
]);
$mrr = json_decode($response->getBody(), true);
// Contains: mrr, active_mrr, wip_mrr, agency_mrr, etc.

Call Logs

CRM-style call log entries linked to customers. Record phone call subjects and notes.

GET/call-logs
POST/call-logs
GET/call-logs/{id}

List call logs

Query Parameters

ParameterTypeDescription
customer_id requiredintegerGet call logs for this customer
curl "{{BASE_URL}}/call-logs?customer_id=1234" \
  -H "Authorization: Bearer $TOKEN"
$response = $client->get('/call-logs', [
    'headers' => ['Authorization' => 'Bearer ' . $token],
    'query'   => ['customer_id' => 1234],
]);
$logs = json_decode($response->getBody(), true)['hydra:member'];

Create a call log

curl -X POST "{{BASE_URL}}/call-logs" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "customer_id": 1234,
    "subject": "Contract renewal discussion",
    "additional_information": "Customer wants to upgrade to 1Gbps",
    "caller_name": "Jan de Vries"
  }'
$response = $client->post('/call-logs', [
    'headers' => ['Authorization' => 'Bearer ' . $token],
    'json'    => [
        'customer_id'            => 1234,
        'subject'                => 'Contract renewal discussion',
        'additional_information' => 'Customer wants to upgrade to 1Gbps',
        'caller_name'            => 'Jan de Vries',
    ],
]);

Uploads

Download files that have been uploaded in the portal (e.g., invoice attachments). This is a read-only resource.

GET/uploads/{id}/download

Download an uploaded file

curl "{{BASE_URL}}/uploads/789/download" \
  -H "Authorization: Bearer $TOKEN" \
  -o downloaded_file.pdf
$response = $client->get('/uploads/789/download', [
    'headers' => ['Authorization' => 'Bearer ' . $token],
]);
file_put_contents('downloaded_file.pdf', $response->getBody());

Service Types

Service types categorize orders (services) into groups like Internet, VoIP, Mobile, etc. They determine which ledger accounts are used for usage exports to accounting software.

GET/service-types
POST/service-types
GET/service-types/{id}
PATCH/service-types/{id}

List service types

curl "{{BASE_URL}}/service-types" \
  -H "Authorization: Bearer $TOKEN"
$response = $client->get('/service-types', [
    'headers' => ['Authorization' => 'Bearer ' . $token],
]);
$serviceTypes = json_decode($response->getBody(), true);

Suppliers

Read-only directory of suppliers (e.g., KPN, VodafoneZiggo) that provide services to resellers.

GET/suppliers
GET/suppliers/{id}

List all suppliers

curl "{{BASE_URL}}/suppliers" \
  -H "Authorization: Bearer $TOKEN"
$response = $client->get('/suppliers', [
    'headers' => ['Authorization' => 'Bearer ' . $token],
]);
$suppliers = json_decode($response->getBody(), true);

Customer Types

Read-only reference data for customer types (e.g., end customer, dealer, wholesaler). Determines access level and billing behavior.

GET/customer-types
GET/customer-types/{id}

List customer types

curl "{{BASE_URL}}/customer-types" \
  -H "Authorization: Bearer $TOKEN"
$response = $client->get('/customer-types', [
    'headers' => ['Authorization' => 'Bearer ' . $token],
]);
$types = json_decode($response->getBody(), true);

Contact Preferences

Manage customer contact preferences. This is one of the few resources that supports DELETE operations.

GET/contact-preferences
POST/contact-preferences
GET/contact-preferences/{id}
PATCH/contact-preferences/{id}
DELETE/contact-preferences/{id}

Manage contact preferences

# List contact preferences
curl "{{BASE_URL}}/contact-preferences" \
  -H "Authorization: Bearer $TOKEN"

# Delete a contact preference
curl -X DELETE "{{BASE_URL}}/contact-preferences/123" \
  -H "Authorization: Bearer $TOKEN"
// List contact preferences
$response = $client->get('/contact-preferences', [
    'headers' => ['Authorization' => 'Bearer ' . $token],
]);

// Delete a contact preference
$client->delete('/contact-preferences/123', [
    'headers' => ['Authorization' => 'Bearer ' . $token],
]);

Locations

Retrieve location data and ISRA (Infrastructure Standard Reference for Addresses) information for Dutch addresses. Useful for checking fiber/copper availability at a specific address.

GET/locations/{id}
GET/locations/getIsraInformation/{zipcode}/{housenumber}

ISRA Lookup Parameters

ParameterTypeDescription
zipcode (path)stringDutch postal code (e.g., 1234AB)
housenumber (path)stringHouse number
housenumberExtensionstringHouse number extension (e.g., A, bis)
xdfServiceIdstringOptional XDF service ID for specific service lookup

Check infrastructure at an address

# Get ISRA information (fiber/copper availability)
curl "{{BASE_URL}}/locations/getIsraInformation/1234AB/10?housenumberExtension=A" \
  -H "Authorization: Bearer $TOKEN"

# Response includes carrier type (COPPER/FIBER), FPI, SDF, MDF data
$response = $client->get('/locations/getIsraInformation/1234AB/10', [
    'headers' => ['Authorization' => 'Bearer ' . $token],
    'query'   => ['housenumberExtension' => 'A'],
]);
$isra = json_decode($response->getBody(), true);
// $isra contains carrier type (COPPER/FIBER), FPI, SDF, MDF data

Order Details Overview

Each Order (service) has exactly one Order Details sub-resource that contains service-type-specific technical information. The type of detail depends on the service type of the order.

Service TypeDetail ResourceContains
Generic / other/order-details-generics/{id}Free-form custom fields
Internet (connection)/order-details-connections/{id}Circuit IDs, connection parameters
Odido Mobile/order-details-odido-mobile/{id}SIM, IMSI, phone number
X2VoIP/order-details-x2voips/{id}SIP credentials, trunk config, credit
X2Mobile/order-details-x2mobiles/{id}SIM, phone number, data usage
Youfone/order-details-youfones/{id}Network modules, SMS settings
KPN WBA/order-details-kpn-wba-by-order/{orderId}Line profile, circuit info
Allsetra Tracking/order-details-tracked-objects/{id}Tracking device, subscription

How to find the detail for an order

When you GET /orders/{id}, the response includes an IRI reference to the typed detail resource (e.g., "orderDetailsGeneric": "/order-details-generics/5678"). Follow that IRI to retrieve the service-specific details.

X2VoIP orders also have sub-resources for their numbers (/order-details-x2voip-numbers), eFaxes (/order-details-x2voip-efaxes), and scheduled forwards (/order-details-x2voip-numbers-scheduled-forwards).

Order Details: Connections

Connection-level details for an order (service). Contains technical connection information such as circuit IDs and connection parameters.

GET/order-details-connections/{id}

Get connection details

curl "{{BASE_URL}}/order-details-connections/123" \
  -H "Authorization: Bearer $TOKEN"
$response = $client->get('/order-details-connections/123', [
    'headers' => ['Authorization' => 'Bearer ' . $token],
]);
$connection = json_decode($response->getBody(), true);

Order Details: Generics

Generic order details for services that don't have a specialized detail type. Can contain free-form data and custom fields.

GET/order-details-generics/{id}
PATCH/order-details-generics/{id}

Get generic order details

curl "{{BASE_URL}}/order-details-generics/456" \
  -H "Authorization: Bearer $TOKEN"
$response = $client->get('/order-details-generics/456', [
    'headers' => ['Authorization' => 'Bearer ' . $token],
]);
$details = json_decode($response->getBody(), true);

Order Details: Odido Mobile

Service-specific details for Odido (formerly T-Mobile) mobile subscriptions. Contains SIM, IMSI, phone number, and subscription-level configuration.

GET/order-details-odido-mobile/{id}
PATCH/order-details-odido-mobile/{id}

Get Odido mobile details

curl "{{BASE_URL}}/order-details-odido-mobile/789" \
  -H "Authorization: Bearer $TOKEN"
$response = $client->get('/order-details-odido-mobile/789', [
    'headers' => ['Authorization' => 'Bearer ' . $token],
]);
$odido = json_decode($response->getBody(), true);

Order Details: X2VoIP

VoIP service details for X2VoIP accounts. Contains SIP credentials, trunk configuration, credit balance, and registration status. Supports listing, viewing, updating, and checking low credit accounts and SIP registration status.

GET/order-details-x2voips
GET/order-details-x2voips/{id}
PATCH/order-details-x2voips/{id}
GET/order-details-x2voips/low-credit-accounts
GET/order-details-x2voips/registration/{id}

List X2VoIP details and check registration

# List X2VoIP accounts
curl "{{BASE_URL}}/order-details-x2voips" \
  -H "Authorization: Bearer $TOKEN"

# Check SIP registration status
curl "{{BASE_URL}}/order-details-x2voips/registration/123" \
  -H "Authorization: Bearer $TOKEN"

# Get accounts with low credit
curl "{{BASE_URL}}/order-details-x2voips/low-credit-accounts" \
  -H "Authorization: Bearer $TOKEN"
// List X2VoIP accounts
$response = $client->get('/order-details-x2voips', [
    'headers' => ['Authorization' => 'Bearer ' . $token],
]);
$accounts = json_decode($response->getBody(), true);

// Check SIP registration
$response = $client->get('/order-details-x2voips/registration/123', [
    'headers' => ['Authorization' => 'Bearer ' . $token],
]);
$registration = json_decode($response->getBody(), true);

Order Details: X2VoIP Numbers

Phone numbers assigned to X2VoIP accounts. Each number has routing, forwarding, and call handling configuration. Numbers can be split from blocks.

GET/order-details-x2voip-numbers/{id}
GET/order-details-x2voip-number/{id}/split

Get X2VoIP number details

curl "{{BASE_URL}}/order-details-x2voip-numbers/456" \
  -H "Authorization: Bearer $TOKEN"
$response = $client->get('/order-details-x2voip-numbers/456', [
    'headers' => ['Authorization' => 'Bearer ' . $token],
]);
$number = json_decode($response->getBody(), true);

Order Details: X2VoIP eFaxes

eFax (electronic fax) numbers linked to X2VoIP accounts. Supports viewing and removing eFax numbers.

GET/order-details-x2voip-efaxes/{id}
DELETE/order-details-x2voip-efaxes/{id}

Get eFax details

curl "{{BASE_URL}}/order-details-x2voip-efaxes/789" \
  -H "Authorization: Bearer $TOKEN"
$response = $client->get('/order-details-x2voip-efaxes/789', [
    'headers' => ['Authorization' => 'Bearer ' . $token],
]);
$efax = json_decode($response->getBody(), true);

Order Details: X2VoIP Scheduled Forwards

Manage time-based call forwarding schedules for X2VoIP numbers. Allows creating, updating, and deleting forwarding rules that activate at specific times.

GET/order-details-x2voip-numbers-scheduled-forwards
POST/order-details-x2voip-numbers-scheduled-forwards
GET/order-details-x2voip-numbers-scheduled-forwards/{id}
PUT/order-details-x2voip-numbers-scheduled-forwards/{id}
PATCH/order-details-x2voip-numbers-scheduled-forwards/{id}
DELETE/order-details-x2voip-numbers-scheduled-forwards/{id}

Create a scheduled forward

# List scheduled forwards
curl "{{BASE_URL}}/order-details-x2voip-numbers-scheduled-forwards" \
  -H "Authorization: Bearer $TOKEN"

# Create a scheduled forward
curl -X POST "{{BASE_URL}}/order-details-x2voip-numbers-scheduled-forwards" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"orderDetailsX2voipNumber": "/order-details-x2voip-numbers/456", "forwardTo": "0612345678"}'
// List scheduled forwards
$response = $client->get('/order-details-x2voip-numbers-scheduled-forwards', [
    'headers' => ['Authorization' => 'Bearer ' . $token],
]);
$forwards = json_decode($response->getBody(), true);

// Create a scheduled forward
$response = $client->post('/order-details-x2voip-numbers-scheduled-forwards', [
    'headers' => [
        'Authorization' => 'Bearer ' . $token,
        'Content-Type'  => 'application/json',
    ],
    'json' => [
        'orderDetailsX2voipNumber' => '/order-details-x2voip-numbers/456',
        'forwardTo' => '0612345678',
    ],
]);
$forward = json_decode($response->getBody(), true);

Order Details: X2Mobile

Mobile service details for X2Mobile SIM-based subscriptions. Contains SIM data, phone number, and data usage tracking. Supports checking current-month data usage per phone number.

GET/order-details-x2mobiles
GET/order-details-x2mobiles/{id}
PATCH/order-details-x2mobiles/{id}
GET/order-details-x2mobile/current-month-data-usage/{phoneNumber}

Get X2Mobile details and data usage

# List X2Mobile services
curl "{{BASE_URL}}/order-details-x2mobiles" \
  -H "Authorization: Bearer $TOKEN"

# Check current month data usage
curl "{{BASE_URL}}/order-details-x2mobile/current-month-data-usage/0612345678" \
  -H "Authorization: Bearer $TOKEN"
// List X2Mobile services
$response = $client->get('/order-details-x2mobiles', [
    'headers' => ['Authorization' => 'Bearer ' . $token],
]);
$mobiles = json_decode($response->getBody(), true);

// Check current month data usage
$response = $client->get('/order-details-x2mobile/current-month-data-usage/0612345678', [
    'headers' => ['Authorization' => 'Bearer ' . $token],
]);
$usage = json_decode($response->getBody(), true);

Order Details: Youfone

Service details for Youfone mobile subscriptions. Supports viewing settings, changing network modules, managing email addresses, viewing operational actions, and managing SMS notifications.

GET/order-details-youfones/{id}
POST/order-details-youfones/{id}/change-network-modules
PATCH/order-details-youfones/{id}/edit-email-address
GET/order-details-youfones/{id}/network-modules-details
GET/order-details-youfones/{id}/operational-actions
GET/order-details-youfones/{id}/sms-notifications
GET/order-details-youfones/{id}/youfone-change-subscriptions

Get Youfone service details

# Get Youfone details
curl "{{BASE_URL}}/order-details-youfones/123" \
  -H "Authorization: Bearer $TOKEN"

# Get network modules
curl "{{BASE_URL}}/order-details-youfones/123/network-modules-details" \
  -H "Authorization: Bearer $TOKEN"
$response = $client->get('/order-details-youfones/123', [
    'headers' => ['Authorization' => 'Bearer ' . $token],
]);
$youfone = json_decode($response->getBody(), true);

// Get network module details
$response = $client->get('/order-details-youfones/123/network-modules-details', [
    'headers' => ['Authorization' => 'Bearer ' . $token],
]);
$modules = json_decode($response->getBody(), true);

Order Details: KPN WBA

KPN Wholesale Broadband Access (WBA) connection details for an order. Retrieve technical details such as line profile, connection status, and circuit information.

GET/order-details-kpn-wba-by-order/{orderId}

Get KPN WBA details by order

curl "{{BASE_URL}}/order-details-kpn-wba-by-order/789" \
  -H "Authorization: Bearer $TOKEN"
$response = $client->get('/order-details-kpn-wba-by-order/789', [
    'headers' => ['Authorization' => 'Bearer ' . $token],
]);
$kpnWba = json_decode($response->getBody(), true);

Order Details: Tracked Objects

Allsetra tracked object details linked to an order. Supports creating, updating, activating subscriptions, moving objects to different customers, and terminating tracked objects.

GET/order-details-tracked-objects
POST/order-details-tracked-objects
GET/order-details-tracked-objects/{id}
PATCH/order-details-tracked-objects/{id}
POST/order-details-tracked-objects/{id}/activate-subscription
POST/order-details-tracked-objects/{id}/move-to-customer
POST/order-details-tracked-objects/{id}/terminate

Manage tracked objects

# List tracked objects
curl "{{BASE_URL}}/order-details-tracked-objects" \
  -H "Authorization: Bearer $TOKEN"

# Activate a subscription for a tracked object
curl -X POST "{{BASE_URL}}/order-details-tracked-objects/123/activate-subscription" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"product": "/products/42"}'
// List tracked objects
$response = $client->get('/order-details-tracked-objects', [
    'headers' => ['Authorization' => 'Bearer ' . $token],
]);
$objects = json_decode($response->getBody(), true);

// Activate a subscription
$response = $client->post('/order-details-tracked-objects/123/activate-subscription', [
    'headers' => [
        'Authorization' => 'Bearer ' . $token,
        'Content-Type'  => 'application/json',
    ],
    'json' => ['product' => '/products/42'],
]);
$result = json_decode($response->getBody(), true);

Tracked Object Tracking Devices

Links between tracked objects and their physical tracking devices. Shows which device is installed in which tracked object.

GET/tracked-object-tracking-devices
GET/tracked-object-tracking-devices/{id}

List device assignments

curl "{{BASE_URL}}/tracked-object-tracking-devices" \
  -H "Authorization: Bearer $TOKEN"
$response = $client->get('/tracked-object-tracking-devices', [
    'headers' => ['Authorization' => 'Bearer ' . $token],
]);
$devices = json_decode($response->getBody(), true);

Tracking Device Outages

Report and retrieve outage information for tracking devices. Useful for monitoring device health and connectivity.

GET/tracking-device-outages/report

Get outage report

curl "{{BASE_URL}}/tracking-device-outages/report" \
  -H "Authorization: Bearer $TOKEN"
$response = $client->get('/tracking-device-outages/report', [
    'headers' => ['Authorization' => 'Bearer ' . $token],
]);
$outages = json_decode($response->getBody(), true);

Outports

Outport (number porting out) requests. When a customer is porting their phone numbers to another provider, these requests appear here. Supports approving or denying outport requests.

GET/outports
GET/outports/{id}
POST/outports/accept-outport
POST/outports/deny-outport

Handle outport requests

# List outport requests
curl "{{BASE_URL}}/outports" \
  -H "Authorization: Bearer $TOKEN"

# Approve an outport
curl -X POST "{{BASE_URL}}/outports/accept-outport" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"id": 123}'
// List outport requests
$response = $client->get('/outports', [
    'headers' => ['Authorization' => 'Bearer ' . $token],
]);
$outports = json_decode($response->getBody(), true);

// Approve an outport
$response = $client->post('/outports/accept-outport', [
    'headers' => [
        'Authorization' => 'Bearer ' . $token,
        'Content-Type'  => 'application/json',
    ],
    'json' => ['id' => 123],
]);
$result = json_decode($response->getBody(), true);

X2VoIP Routes

Inbound call routes for X2VoIP accounts. Define how incoming calls are handled (ring group, IVR, voicemail, etc.).

GET/x2voip-routes
GET/x2voip-routes/{id}

List routes

curl "{{BASE_URL}}/x2voip-routes" \
  -H "Authorization: Bearer $TOKEN"
$response = $client->get('/x2voip-routes', [
    'headers' => ['Authorization' => 'Bearer ' . $token],
]);
$routes = json_decode($response->getBody(), true);

X2VoIP Outbound Routes

Outbound call routing rules for X2VoIP accounts. Control how outgoing calls are routed through different trunks or carriers.

GET/x2voip-outbound-routes
GET/x2voip-outbound-routes/{id}

List outbound routes

curl "{{BASE_URL}}/x2voip-outbound-routes" \
  -H "Authorization: Bearer $TOKEN"
$response = $client->get('/x2voip-outbound-routes', [
    'headers' => ['Authorization' => 'Bearer ' . $token],
]);
$outboundRoutes = json_decode($response->getBody(), true);

X2VoIP Call Barrings

Call barring rules for X2VoIP accounts. Define which call types should be blocked (e.g., international, premium rate).

GET/x2voip-call-barrings
GET/x2voip-call-barrings/{id}

List call barrings

curl "{{BASE_URL}}/x2voip-call-barrings" \
  -H "Authorization: Bearer $TOKEN"
$response = $client->get('/x2voip-call-barrings', [
    'headers' => ['Authorization' => 'Bearer ' . $token],
]);
$barrings = json_decode($response->getBody(), true);

Horizon XDRs

Search Horizon SBC (Session Border Controller) eXtended Detail Records. Provides detailed call records for Horizon-based telephony services.

GET/horizon-xdrs/search

Search Horizon XDRs

curl "{{BASE_URL}}/horizon-xdrs/search?customerId=1234" \
  -H "Authorization: Bearer $TOKEN"
$response = $client->get('/horizon-xdrs/search', [
    'headers' => ['Authorization' => 'Bearer ' . $token],
    'query'   => ['customerId' => 1234],
]);
$xdrs = json_decode($response->getBody(), true);

Contact Person Types

Reference data for contact person types. Defines the roles a contact person can have (e.g., "Technical", "Billing", "Commercial"). Also includes user-specific contact person type assignments.

GET/contact-person-types
GET/contact-person-types/{id}

List contact person types

curl "{{BASE_URL}}/contact-person-types" \
  -H "Authorization: Bearer $TOKEN"
$response = $client->get('/contact-person-types', [
    'headers' => ['Authorization' => 'Bearer ' . $token],
]);
$types = json_decode($response->getBody(), true);