Developers

API reference

Every endpoint, generated from the API's OpenAPI documents: what it needs, what it returns, and a request you can paste.

Base URL

https://partners.netavo.com/api

Authentication

Authorization: Bearer $THINGSIM_API_KEY

Get a key and pick its scopes. Each endpoint below names the scope it needs.

IoT API · v1

IoT

Manage SIMs: list and inspect them, activate, pause, resume and cease them, change their plan, and read usage, sessions and eSIM activation codes.

Plans

GET/v1/iot/plans

List plans

The plans your account can put a SIM on, with your prices (ex VAT). Use a plan's id with the change-plan endpoint. A SIM can only move to a plan on the coverage it is already on.

Scope
iot.plans:read

Parameters

  • coverage string query
    Only plans with this coverage key.

    Example 3e8d1c5a-7f2b-4a90-b6e4-1d0c9f8a2b37

  • organisationId string (uuid) query
    Partners: the plans and prices of a customer organisation below you.

    Example 0f8b6c1e-5a2d-4c7e-9b3a-2d1e6f4a7c90

  • iccid string query
    Only the plans this SIM can move to (its coverage, at its holder's prices). Other filters are ignored.

    Example 8944110068212345678

Returns 200 PlanList

Every plan available to you.

Errors

  • 400validation-failedValidation failed.
  • 401unauthorizedThe bearer token is missing, expired or revoked.
  • 403insufficient-scopeThe key is valid but lacks the iot.plans:read scope.
  • 429rate-limitedRate limit exceeded. Wait for Retry-After seconds.

curl

curl "https://partners.netavo.com/api/v1/iot/plans" \
  -H "Authorization: Bearer $THINGSIM_API_KEY"

Python

import os
import requests

response = requests.get(
    "https://partners.netavo.com/api/v1/iot/plans",
    headers={
        "Authorization": f"Bearer {os.environ['THINGSIM_API_KEY']}",
    },
)
response.raise_for_status()
print(response.json())

Node

const response = await fetch("https://partners.netavo.com/api/v1/iot/plans", {
  headers: {
    Authorization: `Bearer ${process.env.THINGSIM_API_KEY}`,
  },
});
if (!response.ok) throw new Error(await response.text());
console.log(await response.json());
Response · 200
{
  "data": [
    {
      "id": "5b0e4f2a-8c1d-4e6f-9a3b-7d2c1e0f4a61",
      "name": "1 GB, UK 4-network",
      "coverage": {
        "key": "3e8d1c5a-7f2b-4a90-b6e4-1d0c9f8a2b37",
        "label": "UK 4-network",
        "networks": [
          "EE",
          "O2",
          "Three",
          "Vodafone"
        ]
      },
      "allowanceBytes": 1073741824,
      "monthlyPrice": {
        "amount": "2.40",
        "currency": "GBP",
        "includesVat": false
      },
      "billingIntervals": [
        "monthly"
      ]
    }
  ]
}
Example error · 400
400 · application/problem+json
{
  "type": "https://partners.netavo.com/api/problems/validation-failed",
  "title": "Validation failed",
  "status": 400,
  "detail": "limit must be between 1 and 100.",
  "instance": "/api/v1/iot/plans",
  "code": "validation-failed",
  "traceId": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"
}

SIMs

GET/v1/iot/sims

List SIMs

Every SIM in your account, ordered by ICCID. Partners see SIMs across their customer organisations; organisationId narrows to one of them.

Scope
iot.sims:read

Parameters

  • limit integer query
    How many items to return, 1 to 100.

    Example 50

  • startingAfter string query
    Cursor from the previous page's nextCursor. Leave out for the first page.

    Example c2ltXzg5NDQxMTAwNjgyMTIzNDU2Nzg

  • status string query
    Only SIMs in this status, e.g. active or paused.

    Example active

  • tag string query
    Only SIMs carrying this tag.

    Example site:leeds

  • organisationId string (uuid) query
    Partners: only SIMs held by this organisation (one of yours, or a customer below you).

    Example 0f8b6c1e-5a2d-4c7e-9b3a-2d1e6f4a7c90

Returns 200 SimList

A page of SIMs.

Errors

  • 400validation-failedValidation failed.
  • 401unauthorizedThe bearer token is missing, expired or revoked.
  • 403insufficient-scopeThe key is valid but lacks the iot.sims:read scope.
  • 429rate-limitedRate limit exceeded. Wait for Retry-After seconds.

curl

curl "https://partners.netavo.com/api/v1/iot/sims?limit=50" \
  -H "Authorization: Bearer $THINGSIM_API_KEY"

Python

import os
import requests

response = requests.get(
    "https://partners.netavo.com/api/v1/iot/sims",
    headers={
        "Authorization": f"Bearer {os.environ['THINGSIM_API_KEY']}",
    },
    params={"limit": 50},
)
response.raise_for_status()
print(response.json())

Node

const response = await fetch("https://partners.netavo.com/api/v1/iot/sims?limit=50", {
  headers: {
    Authorization: `Bearer ${process.env.THINGSIM_API_KEY}`,
  },
});
if (!response.ok) throw new Error(await response.text());
console.log(await response.json());
Response · 200
{
  "data": [
    {
      "iccid": "8944110068212345678",
      "label": "Car park barrier 3",
      "tags": [
        "site:leeds",
        "barrier"
      ],
      "status": "active",
      "suspendedReason": null,
      "canResume": false,
      "formFactor": "triSim",
      "plan": {
        "id": "5b0e4f2a-8c1d-4e6f-9a3b-7d2c1e0f4a61",
        "name": "1 GB, UK 4-network",
        "allowanceBytes": 1073741824
      },
      "msisdn": "447700900123",
      "imsi": "234500012345678",
      "ipAddress": "10.64.12.34",
      "organisationId": "0f8b6c1e-5a2d-4c7e-9b3a-2d1e6f4a7c90",
      "activatedAt": "2026-09-01T08:15:00Z",
      "activationScheduledFor": null,
      "stopsAt": null,
      "createdAt": "2026-08-20T14:02:11Z",
      "updatedAt": "2026-09-24T17:40:03Z",
      "canActivate": false,
      "planStartedAt": "2026-09-01T00:00:00Z",
      "scheduledPlanChange": {
        "plan": null,
        "effectiveAt": "2026-10-01T00:00:00Z"
      }
    }
  ],
  "hasMore": true,
  "nextCursor": "c2ltXzg5NDQxMTAwNjgyMTIzNDU2Nzg"
}
Example error · 400
400 · application/problem+json
{
  "type": "https://partners.netavo.com/api/problems/validation-failed",
  "title": "Validation failed",
  "status": 400,
  "detail": "limit must be between 1 and 100.",
  "instance": "/api/v1/iot/sims",
  "code": "validation-failed",
  "traceId": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"
}

GET/v1/iot/sims/{iccid}

Get a SIM

One SIM by ICCID.

Scope
iot.sims:read

Parameters

  • iccid string path required
    The SIM's ICCID, 19 or 20 digits, as printed on the card.

    Example 8944110068212345678

Returns 200 Sim

The SIM.

Errors

  • 401unauthorizedThe bearer token is missing, expired or revoked.
  • 403insufficient-scopeThe key is valid but lacks the iot.sims:read scope.
  • 404not-foundNot found, or not in your account. Resources outside your organisation are always 404, never 403.
  • 429rate-limitedRate limit exceeded. Wait for Retry-After seconds.

curl

curl "https://partners.netavo.com/api/v1/iot/sims/8944110068212345678" \
  -H "Authorization: Bearer $THINGSIM_API_KEY"

Python

import os
import requests

response = requests.get(
    "https://partners.netavo.com/api/v1/iot/sims/8944110068212345678",
    headers={
        "Authorization": f"Bearer {os.environ['THINGSIM_API_KEY']}",
    },
)
response.raise_for_status()
print(response.json())

Node

const response = await fetch("https://partners.netavo.com/api/v1/iot/sims/8944110068212345678", {
  headers: {
    Authorization: `Bearer ${process.env.THINGSIM_API_KEY}`,
  },
});
if (!response.ok) throw new Error(await response.text());
console.log(await response.json());
Response · 200
{
  "iccid": "8944110068212345678",
  "label": "Car park barrier 3",
  "tags": [
    "site:leeds",
    "barrier"
  ],
  "status": "active",
  "suspendedReason": null,
  "canResume": false,
  "formFactor": "triSim",
  "plan": {
    "id": "5b0e4f2a-8c1d-4e6f-9a3b-7d2c1e0f4a61",
    "name": "1 GB, UK 4-network",
    "allowanceBytes": 1073741824
  },
  "msisdn": "447700900123",
  "imsi": "234500012345678",
  "ipAddress": "10.64.12.34",
  "organisationId": "0f8b6c1e-5a2d-4c7e-9b3a-2d1e6f4a7c90",
  "activatedAt": "2026-09-01T08:15:00Z",
  "activationScheduledFor": null,
  "stopsAt": null,
  "createdAt": "2026-08-20T14:02:11Z",
  "updatedAt": "2026-09-24T17:40:03Z",
  "canActivate": false,
  "planStartedAt": "2026-09-01T00:00:00Z",
  "scheduledPlanChange": {
    "plan": {
      "id": "5b0e4f2a-8c1d-4e6f-9a3b-7d2c1e0f4a61",
      "name": "1 GB, UK 4-network",
      "allowanceBytes": 1073741824
    },
    "effectiveAt": "2026-10-01T00:00:00Z"
  }
}
Example error · 404
404 · application/problem+json
{
  "type": "https://partners.netavo.com/api/problems/not-found",
  "title": "Not found",
  "status": 404,
  "detail": "No such resource in your account.",
  "instance": "/api/v1/iot/sims/{iccid}",
  "code": "not-found",
  "traceId": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"
}

PATCH/v1/iot/sims/{iccid}

Update a SIM

Sets the SIM's label and tags. Leave a field out (or send null) to keep it; "label": "" clears the label and "tags": [] removes every tag. Tags are de-duplicated ignoring case; at most 20 of up to 50 characters each.

Scope
iot.sims:write

Parameters

  • iccid string path required
    The SIM's ICCID, 19 or 20 digits, as printed on the card.

    Example 8944110068212345678

Request body SimUpdate

  • label string or null
    New label; "" clears it. Null or left out keeps it (so a client that always sends every field cannot wipe a label by accident).

    Example Car park barrier 3

  • tags string[] or null
    Replaces all tags; [] removes them. Null or left out keeps them.

    Example ["site:leeds","barrier"]

Returns 200 Sim

The updated SIM.

Errors

  • 400validation-failedValidation failed.
  • 401unauthorizedThe bearer token is missing, expired or revoked.
  • 403insufficient-scopeThe key is valid but lacks the iot.sims:write scope.
  • 404not-foundNot found, or not in your account. Resources outside your organisation are always 404, never 403.
  • 429rate-limitedRate limit exceeded. Wait for Retry-After seconds.

curl

curl -X PATCH "https://partners.netavo.com/api/v1/iot/sims/8944110068212345678" \
  -H "Authorization: Bearer $THINGSIM_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "label": "Car park barrier 3",
  "tags": [
    "site:leeds",
    "barrier"
  ]
}'

Python

import os
import requests

response = requests.patch(
    "https://partners.netavo.com/api/v1/iot/sims/8944110068212345678",
    headers={
        "Authorization": f"Bearer {os.environ['THINGSIM_API_KEY']}",
    },
    json={
        "label": "Car park barrier 3",
        "tags": ["site:leeds", "barrier"],
    },
)
response.raise_for_status()
print(response.json())

Node

const response = await fetch("https://partners.netavo.com/api/v1/iot/sims/8944110068212345678", {
  method: "PATCH",
  headers: {
    Authorization: `Bearer ${process.env.THINGSIM_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "label": "Car park barrier 3",
    "tags": [
      "site:leeds",
      "barrier"
    ]
  }),
});
if (!response.ok) throw new Error(await response.text());
console.log(await response.json());
Response · 200
{
  "iccid": "8944110068212345678",
  "label": "Car park barrier 3",
  "tags": [
    "site:leeds",
    "barrier"
  ],
  "status": "active",
  "suspendedReason": null,
  "canResume": false,
  "formFactor": "triSim",
  "plan": {
    "id": "5b0e4f2a-8c1d-4e6f-9a3b-7d2c1e0f4a61",
    "name": "1 GB, UK 4-network",
    "allowanceBytes": 1073741824
  },
  "msisdn": "447700900123",
  "imsi": "234500012345678",
  "ipAddress": "10.64.12.34",
  "organisationId": "0f8b6c1e-5a2d-4c7e-9b3a-2d1e6f4a7c90",
  "activatedAt": "2026-09-01T08:15:00Z",
  "activationScheduledFor": null,
  "stopsAt": null,
  "createdAt": "2026-08-20T14:02:11Z",
  "updatedAt": "2026-09-24T17:40:03Z",
  "canActivate": false,
  "planStartedAt": "2026-09-01T00:00:00Z",
  "scheduledPlanChange": {
    "plan": {
      "id": "5b0e4f2a-8c1d-4e6f-9a3b-7d2c1e0f4a61",
      "name": "1 GB, UK 4-network",
      "allowanceBytes": 1073741824
    },
    "effectiveAt": "2026-10-01T00:00:00Z"
  }
}
Example error · 400
400 · application/problem+json
{
  "type": "https://partners.netavo.com/api/problems/validation-failed",
  "title": "Validation failed",
  "status": 400,
  "detail": "limit must be between 1 and 100.",
  "instance": "/api/v1/iot/sims/{iccid}",
  "code": "validation-failed",
  "traceId": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"
}

POST/v1/iot/sims/{iccid}/activate

Activate a SIM

Starts service on an inactive SIM that has a plan. Activation is scheduled for the next working day (activationScheduledFor); billing starts when it takes effect and iot.sim.activated is sent then.

Scope
iot.sims:write
Idempotent
Send an Idempotency-Key header

Parameters

  • iccid string path required
    The SIM's ICCID, 19 or 20 digits, as printed on the card.

    Example 8944110068212345678

Returns 202 Sim

Accepted. The SIM is returned in its pending state; a notification follows when the change takes effect.

Errors

  • 401unauthorizedThe bearer token is missing, expired or revoked.
  • 403insufficient-scopeThe key is valid but lacks the iot.sims:write scope.
  • 404not-foundNot found, or not in your account. Resources outside your organisation are always 404, never 403.
  • 409invalid-stateThe SIM is not in a state this action applies to.
  • 422idempotency-key-reusedThe Idempotency-Key was already used for a different request.
  • 429rate-limitedRate limit exceeded. Wait for Retry-After seconds.

curl

curl -X POST "https://partners.netavo.com/api/v1/iot/sims/8944110068212345678/activate" \
  -H "Authorization: Bearer $THINGSIM_API_KEY" \
  -H "Idempotency-Key: 5d1c7a0e-2f4b-4e8a-9c61-3b7f0d2e8a14"

Python

import os
import requests

response = requests.post(
    "https://partners.netavo.com/api/v1/iot/sims/8944110068212345678/activate",
    headers={
        "Authorization": f"Bearer {os.environ['THINGSIM_API_KEY']}",
        "Idempotency-Key": "5d1c7a0e-2f4b-4e8a-9c61-3b7f0d2e8a14",
    },
)
response.raise_for_status()
print(response.json())

Node

const response = await fetch("https://partners.netavo.com/api/v1/iot/sims/8944110068212345678/activate", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.THINGSIM_API_KEY}`,
    "Idempotency-Key": "5d1c7a0e-2f4b-4e8a-9c61-3b7f0d2e8a14",
  },
});
if (!response.ok) throw new Error(await response.text());
console.log(await response.json());
Response · 202
{
  "iccid": "8944110068212345678",
  "label": "Car park barrier 3",
  "tags": [
    "site:leeds",
    "barrier"
  ],
  "status": "active",
  "suspendedReason": null,
  "canResume": false,
  "formFactor": "triSim",
  "plan": {
    "id": "5b0e4f2a-8c1d-4e6f-9a3b-7d2c1e0f4a61",
    "name": "1 GB, UK 4-network",
    "allowanceBytes": 1073741824
  },
  "msisdn": "447700900123",
  "imsi": "234500012345678",
  "ipAddress": "10.64.12.34",
  "organisationId": "0f8b6c1e-5a2d-4c7e-9b3a-2d1e6f4a7c90",
  "activatedAt": "2026-09-01T08:15:00Z",
  "activationScheduledFor": null,
  "stopsAt": null,
  "createdAt": "2026-08-20T14:02:11Z",
  "updatedAt": "2026-09-24T17:40:03Z",
  "canActivate": false,
  "planStartedAt": "2026-09-01T00:00:00Z",
  "scheduledPlanChange": {
    "plan": {
      "id": "5b0e4f2a-8c1d-4e6f-9a3b-7d2c1e0f4a61",
      "name": "1 GB, UK 4-network",
      "allowanceBytes": 1073741824
    },
    "effectiveAt": "2026-10-01T00:00:00Z"
  }
}
Example error · 404
404 · application/problem+json
{
  "type": "https://partners.netavo.com/api/problems/not-found",
  "title": "Not found",
  "status": 404,
  "detail": "No such resource in your account.",
  "instance": "/api/v1/iot/sims/{iccid}/activate",
  "code": "not-found",
  "traceId": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"
}

POST/v1/iot/sims/{iccid}/cease

Cease a SIM

Permanently ends service on a SIM. The SIM keeps working, and billing, until the end of the period already paid for (stopsAt); nothing is refunded, and an annual SIM stays billed to the end of its paid year. iot.sim.ceased is sent when it stops. A SIM that has never started billing is withdrawn at once. Needs its own scope because it cannot be undone.

Scope
iot.sims:cease
Idempotent
Send an Idempotency-Key header

Parameters

  • iccid string path required
    The SIM's ICCID, 19 or 20 digits, as printed on the card.

    Example 8944110068212345678

Request body SimCeaseRequest

  • confirmIccid string required
    Must equal the ICCID in the path.

    Example 8944110068212345678

  • reason string or null
    Your note, kept in our logs of the SIM's history.

    Example Barrier removed

Returns 202 Sim

Accepted. The SIM is returned as pendingCease with its stopsAt.

Errors

  • 400validation-failedValidation failed.
  • 401unauthorizedThe bearer token is missing, expired or revoked.
  • 403insufficient-scopeThe key is valid but lacks the iot.sims:cease scope.
  • 404not-foundNot found, or not in your account. Resources outside your organisation are always 404, never 403.
  • 409invalid-stateThe SIM is not in a state this action applies to.
  • 422idempotency-key-reusedThe Idempotency-Key was already used for a different request.
  • 429rate-limitedRate limit exceeded. Wait for Retry-After seconds.

curl

curl -X POST "https://partners.netavo.com/api/v1/iot/sims/8944110068212345678/cease" \
  -H "Authorization: Bearer $THINGSIM_API_KEY" \
  -H "Idempotency-Key: 5d1c7a0e-2f4b-4e8a-9c61-3b7f0d2e8a14" \
  -H "Content-Type: application/json" \
  -d '{
  "confirmIccid": "8944110068212345678",
  "reason": "Barrier removed"
}'

Python

import os
import requests

response = requests.post(
    "https://partners.netavo.com/api/v1/iot/sims/8944110068212345678/cease",
    headers={
        "Authorization": f"Bearer {os.environ['THINGSIM_API_KEY']}",
        "Idempotency-Key": "5d1c7a0e-2f4b-4e8a-9c61-3b7f0d2e8a14",
    },
    json={
        "confirmIccid": "8944110068212345678",
        "reason": "Barrier removed",
    },
)
response.raise_for_status()
print(response.json())

Node

const response = await fetch("https://partners.netavo.com/api/v1/iot/sims/8944110068212345678/cease", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.THINGSIM_API_KEY}`,
    "Idempotency-Key": "5d1c7a0e-2f4b-4e8a-9c61-3b7f0d2e8a14",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "confirmIccid": "8944110068212345678",
    "reason": "Barrier removed"
  }),
});
if (!response.ok) throw new Error(await response.text());
console.log(await response.json());
Response · 202
{
  "iccid": "8944110068212345678",
  "label": "Car park barrier 3",
  "tags": [
    "site:leeds",
    "barrier"
  ],
  "status": "active",
  "suspendedReason": null,
  "canResume": false,
  "formFactor": "triSim",
  "plan": {
    "id": "5b0e4f2a-8c1d-4e6f-9a3b-7d2c1e0f4a61",
    "name": "1 GB, UK 4-network",
    "allowanceBytes": 1073741824
  },
  "msisdn": "447700900123",
  "imsi": "234500012345678",
  "ipAddress": "10.64.12.34",
  "organisationId": "0f8b6c1e-5a2d-4c7e-9b3a-2d1e6f4a7c90",
  "activatedAt": "2026-09-01T08:15:00Z",
  "activationScheduledFor": null,
  "stopsAt": null,
  "createdAt": "2026-08-20T14:02:11Z",
  "updatedAt": "2026-09-24T17:40:03Z",
  "canActivate": false,
  "planStartedAt": "2026-09-01T00:00:00Z",
  "scheduledPlanChange": {
    "plan": {
      "id": "5b0e4f2a-8c1d-4e6f-9a3b-7d2c1e0f4a61",
      "name": "1 GB, UK 4-network",
      "allowanceBytes": 1073741824
    },
    "effectiveAt": "2026-10-01T00:00:00Z"
  }
}
Example error · 400
400 · application/problem+json
{
  "type": "https://partners.netavo.com/api/problems/validation-failed",
  "title": "Validation failed",
  "status": 400,
  "detail": "limit must be between 1 and 100.",
  "instance": "/api/v1/iot/sims/{iccid}/cease",
  "code": "validation-failed",
  "traceId": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"
}

POST/v1/iot/sims/{iccid}/pause

Pause a SIM

Stops the SIM passing data until you resume it. The plan keeps running and billing, and iot.sim.paused is sent when the network has applied it. Pausing a SIM that is already paused or suspended changes nothing.

Scope
iot.sims:write
Idempotent
Send an Idempotency-Key header

Parameters

  • iccid string path required
    The SIM's ICCID, 19 or 20 digits, as printed on the card.

    Example 8944110068212345678

Returns 202 Sim

Accepted. The SIM is returned in its pending state; a notification follows when the change takes effect.

Errors

  • 401unauthorizedThe bearer token is missing, expired or revoked.
  • 403insufficient-scopeThe key is valid but lacks the iot.sims:write scope.
  • 404not-foundNot found, or not in your account. Resources outside your organisation are always 404, never 403.
  • 409invalid-stateThe SIM is not in a state this action applies to.
  • 422idempotency-key-reusedThe Idempotency-Key was already used for a different request.
  • 429rate-limitedRate limit exceeded. Wait for Retry-After seconds.

curl

curl -X POST "https://partners.netavo.com/api/v1/iot/sims/8944110068212345678/pause" \
  -H "Authorization: Bearer $THINGSIM_API_KEY" \
  -H "Idempotency-Key: 5d1c7a0e-2f4b-4e8a-9c61-3b7f0d2e8a14"

Python

import os
import requests

response = requests.post(
    "https://partners.netavo.com/api/v1/iot/sims/8944110068212345678/pause",
    headers={
        "Authorization": f"Bearer {os.environ['THINGSIM_API_KEY']}",
        "Idempotency-Key": "5d1c7a0e-2f4b-4e8a-9c61-3b7f0d2e8a14",
    },
)
response.raise_for_status()
print(response.json())

Node

const response = await fetch("https://partners.netavo.com/api/v1/iot/sims/8944110068212345678/pause", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.THINGSIM_API_KEY}`,
    "Idempotency-Key": "5d1c7a0e-2f4b-4e8a-9c61-3b7f0d2e8a14",
  },
});
if (!response.ok) throw new Error(await response.text());
console.log(await response.json());
Response · 202
{
  "iccid": "8944110068212345678",
  "label": "Car park barrier 3",
  "tags": [
    "site:leeds",
    "barrier"
  ],
  "status": "active",
  "suspendedReason": null,
  "canResume": false,
  "formFactor": "triSim",
  "plan": {
    "id": "5b0e4f2a-8c1d-4e6f-9a3b-7d2c1e0f4a61",
    "name": "1 GB, UK 4-network",
    "allowanceBytes": 1073741824
  },
  "msisdn": "447700900123",
  "imsi": "234500012345678",
  "ipAddress": "10.64.12.34",
  "organisationId": "0f8b6c1e-5a2d-4c7e-9b3a-2d1e6f4a7c90",
  "activatedAt": "2026-09-01T08:15:00Z",
  "activationScheduledFor": null,
  "stopsAt": null,
  "createdAt": "2026-08-20T14:02:11Z",
  "updatedAt": "2026-09-24T17:40:03Z",
  "canActivate": false,
  "planStartedAt": "2026-09-01T00:00:00Z",
  "scheduledPlanChange": {
    "plan": {
      "id": "5b0e4f2a-8c1d-4e6f-9a3b-7d2c1e0f4a61",
      "name": "1 GB, UK 4-network",
      "allowanceBytes": 1073741824
    },
    "effectiveAt": "2026-10-01T00:00:00Z"
  }
}
Example error · 404
404 · application/problem+json
{
  "type": "https://partners.netavo.com/api/problems/not-found",
  "title": "Not found",
  "status": 404,
  "detail": "No such resource in your account.",
  "instance": "/api/v1/iot/sims/{iccid}/pause",
  "code": "not-found",
  "traceId": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"
}

POST/v1/iot/sims/{iccid}/plan

Change a SIM's plan

Moves the SIM to another plan your account can use (GET /v1/iot/plans). A plan with at least as much data is an upgrade: it applies now and the difference for the rest of the period is charged now. A smaller plan is a downgrade and applies at the next renewal (effectiveAt). iot.sim.plan_changed is sent when it takes effect.

Scope
iot.sims:write
Idempotent
Send an Idempotency-Key header

Parameters

  • iccid string path required
    The SIM's ICCID, 19 or 20 digits, as printed on the card.

    Example 8944110068212345678

Request body PlanChangeRequest

  • planId string required
    A plan id from GET /v1/iot/plans.

    Example 9c7a2e14-3b5d-4f60-8e21-6a4b0d9c3f75

Returns 200 PlanChange

The plan change.

Errors

  • 400validation-failedValidation failed.
  • 401unauthorizedThe bearer token is missing, expired or revoked.
  • 403insufficient-scopeThe key is valid but lacks the iot.sims:write scope.
  • 404not-foundNot found, or not in your account. Resources outside your organisation are always 404, never 403.
  • 409invalid-stateThe SIM is not in a state this action applies to.
  • 422plan-not-eligibleThe plan is not one your account can put this SIM on. Or: The Idempotency-Key was already used for a different request.
  • 429rate-limitedRate limit exceeded. Wait for Retry-After seconds.

curl

curl -X POST "https://partners.netavo.com/api/v1/iot/sims/8944110068212345678/plan" \
  -H "Authorization: Bearer $THINGSIM_API_KEY" \
  -H "Idempotency-Key: 5d1c7a0e-2f4b-4e8a-9c61-3b7f0d2e8a14" \
  -H "Content-Type: application/json" \
  -d '{
  "planId": "9c7a2e14-3b5d-4f60-8e21-6a4b0d9c3f75"
}'

Python

import os
import requests

response = requests.post(
    "https://partners.netavo.com/api/v1/iot/sims/8944110068212345678/plan",
    headers={
        "Authorization": f"Bearer {os.environ['THINGSIM_API_KEY']}",
        "Idempotency-Key": "5d1c7a0e-2f4b-4e8a-9c61-3b7f0d2e8a14",
    },
    json={
        "planId": "9c7a2e14-3b5d-4f60-8e21-6a4b0d9c3f75",
    },
)
response.raise_for_status()
print(response.json())

Node

const response = await fetch("https://partners.netavo.com/api/v1/iot/sims/8944110068212345678/plan", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.THINGSIM_API_KEY}`,
    "Idempotency-Key": "5d1c7a0e-2f4b-4e8a-9c61-3b7f0d2e8a14",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "planId": "9c7a2e14-3b5d-4f60-8e21-6a4b0d9c3f75"
  }),
});
if (!response.ok) throw new Error(await response.text());
console.log(await response.json());
Response · 200
{
  "iccid": "8944110068212345678",
  "fromPlan": {
    "id": "5b0e4f2a-8c1d-4e6f-9a3b-7d2c1e0f4a61",
    "name": "1 GB, UK 4-network",
    "allowanceBytes": 1073741824
  },
  "toPlan": {
    "id": "5b0e4f2a-8c1d-4e6f-9a3b-7d2c1e0f4a61",
    "name": "1 GB, UK 4-network",
    "allowanceBytes": 1073741824
  },
  "direction": "upgrade",
  "effectiveAt": "2026-09-25T00:00:00Z",
  "message": "Upgraded to 5 GB, UK 4-network; the difference for the rest of the period is charged now."
}
Example error · 400
400 · application/problem+json
{
  "type": "https://partners.netavo.com/api/problems/validation-failed",
  "title": "Validation failed",
  "status": 400,
  "detail": "limit must be between 1 and 100.",
  "instance": "/api/v1/iot/sims/{iccid}/plan",
  "code": "validation-failed",
  "traceId": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"
}

POST/v1/iot/sims/{iccid}/resume

Resume a SIM

Restarts data on a SIM you paused. SIMs suspended for another reason (non-payment, or by our operations team) cannot be resumed here: 409 says why. iot.sim.resumed is sent when the network has applied it.

Scope
iot.sims:write
Idempotent
Send an Idempotency-Key header

Parameters

  • iccid string path required
    The SIM's ICCID, 19 or 20 digits, as printed on the card.

    Example 8944110068212345678

Returns 202 Sim

Accepted. The SIM is returned in its pending state; a notification follows when the change takes effect.

Errors

  • 401unauthorizedThe bearer token is missing, expired or revoked.
  • 403insufficient-scopeThe key is valid but lacks the iot.sims:write scope.
  • 404not-foundNot found, or not in your account. Resources outside your organisation are always 404, never 403.
  • 409invalid-stateThe SIM is not paused by you.
  • 422idempotency-key-reusedThe Idempotency-Key was already used for a different request.
  • 429rate-limitedRate limit exceeded. Wait for Retry-After seconds.

curl

curl -X POST "https://partners.netavo.com/api/v1/iot/sims/8944110068212345678/resume" \
  -H "Authorization: Bearer $THINGSIM_API_KEY" \
  -H "Idempotency-Key: 5d1c7a0e-2f4b-4e8a-9c61-3b7f0d2e8a14"

Python

import os
import requests

response = requests.post(
    "https://partners.netavo.com/api/v1/iot/sims/8944110068212345678/resume",
    headers={
        "Authorization": f"Bearer {os.environ['THINGSIM_API_KEY']}",
        "Idempotency-Key": "5d1c7a0e-2f4b-4e8a-9c61-3b7f0d2e8a14",
    },
)
response.raise_for_status()
print(response.json())

Node

const response = await fetch("https://partners.netavo.com/api/v1/iot/sims/8944110068212345678/resume", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.THINGSIM_API_KEY}`,
    "Idempotency-Key": "5d1c7a0e-2f4b-4e8a-9c61-3b7f0d2e8a14",
  },
});
if (!response.ok) throw new Error(await response.text());
console.log(await response.json());
Response · 202
{
  "iccid": "8944110068212345678",
  "label": "Car park barrier 3",
  "tags": [
    "site:leeds",
    "barrier"
  ],
  "status": "active",
  "suspendedReason": null,
  "canResume": false,
  "formFactor": "triSim",
  "plan": {
    "id": "5b0e4f2a-8c1d-4e6f-9a3b-7d2c1e0f4a61",
    "name": "1 GB, UK 4-network",
    "allowanceBytes": 1073741824
  },
  "msisdn": "447700900123",
  "imsi": "234500012345678",
  "ipAddress": "10.64.12.34",
  "organisationId": "0f8b6c1e-5a2d-4c7e-9b3a-2d1e6f4a7c90",
  "activatedAt": "2026-09-01T08:15:00Z",
  "activationScheduledFor": null,
  "stopsAt": null,
  "createdAt": "2026-08-20T14:02:11Z",
  "updatedAt": "2026-09-24T17:40:03Z",
  "canActivate": false,
  "planStartedAt": "2026-09-01T00:00:00Z",
  "scheduledPlanChange": {
    "plan": {
      "id": "5b0e4f2a-8c1d-4e6f-9a3b-7d2c1e0f4a61",
      "name": "1 GB, UK 4-network",
      "allowanceBytes": 1073741824
    },
    "effectiveAt": "2026-10-01T00:00:00Z"
  }
}
Example error · 404
404 · application/problem+json
{
  "type": "https://partners.netavo.com/api/problems/not-found",
  "title": "Not found",
  "status": 404,
  "detail": "No such resource in your account.",
  "instance": "/api/v1/iot/sims/{iccid}/resume",
  "code": "not-found",
  "traceId": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"
}

Usage

GET/v1/iot/sims/{iccid}/sessions

List a SIM's sessions

Data sessions, newest first: the session still open (if any), then those that ended in the window (default the last 30 days; at most 93 days).

Scope
iot.usage:read

Parameters

  • iccid string path required
    The SIM's ICCID, 19 or 20 digits, as printed on the card.

    Example 8944110068212345678

  • limit integer query
    How many items to return, 1 to 100.

    Example 50

  • startingAfter string query
    Cursor from the previous page's nextCursor. Leave out for the first page.

    Example c2ltXzg5NDQxMTAwNjgyMTIzNDU2Nzg

  • from string (date-time) query
    Only sessions that ended at or after this time. Default: 30 days ago.

    Example 2026-09-01T00:00:00Z

  • to string (date-time) query
    Only sessions that ended before this time. Default: now.

    Example 2026-09-25T00:00:00Z

Returns 200 SessionList

A page of sessions.

Errors

  • 400validation-failedValidation failed.
  • 401unauthorizedThe bearer token is missing, expired or revoked.
  • 403insufficient-scopeThe key is valid but lacks the iot.usage:read scope.
  • 404not-foundNot found, or not in your account. Resources outside your organisation are always 404, never 403.
  • 429rate-limitedRate limit exceeded. Wait for Retry-After seconds.

curl

curl "https://partners.netavo.com/api/v1/iot/sims/8944110068212345678/sessions?limit=50" \
  -H "Authorization: Bearer $THINGSIM_API_KEY"

Python

import os
import requests

response = requests.get(
    "https://partners.netavo.com/api/v1/iot/sims/8944110068212345678/sessions",
    headers={
        "Authorization": f"Bearer {os.environ['THINGSIM_API_KEY']}",
    },
    params={"limit": 50},
)
response.raise_for_status()
print(response.json())

Node

const response = await fetch("https://partners.netavo.com/api/v1/iot/sims/8944110068212345678/sessions?limit=50", {
  headers: {
    Authorization: `Bearer ${process.env.THINGSIM_API_KEY}`,
  },
});
if (!response.ok) throw new Error(await response.text());
console.log(await response.json());
Response · 200
{
  "data": [
    {
      "id": "ses_4f1c2a9e7b3d",
      "iccid": "8944110068212345678",
      "startedAt": "2026-09-25T06:02:41Z",
      "endedAt": "2026-09-25T08:47:12Z",
      "bytesUp": 1843200,
      "bytesDown": 7340032,
      "network": {
        "mcc": "234",
        "mnc": "15",
        "name": "Vodafone UK"
      },
      "radioAccess": "4G",
      "ipAddress": "10.64.12.34",
      "endReason": "userRequest"
    }
  ],
  "hasMore": true,
  "nextCursor": "c2ltXzg5NDQxMTAwNjgyMTIzNDU2Nzg"
}
Example error · 400
400 · application/problem+json
{
  "type": "https://partners.netavo.com/api/problems/validation-failed",
  "title": "Validation failed",
  "status": 400,
  "detail": "limit must be between 1 and 100.",
  "instance": "/api/v1/iot/sims/{iccid}/sessions",
  "code": "validation-failed",
  "traceId": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"
}

GET/v1/iot/sims/{iccid}/usage

Get a SIM's usage

Data used in the current allowance period (a calendar month), including the session still open, with up to twelve earlier periods in history — or one past month with period.

Scope
iot.usage:read

Parameters

  • iccid string path required
    The SIM's ICCID, 19 or 20 digits, as printed on the card.

    Example 8944110068212345678

  • period string query
    A past month as yyyy-mm. Leave out for the current period.

    Example 2026-08

Returns 200 SimUsage

Usage for the period.

Errors

  • 400validation-failedValidation failed.
  • 401unauthorizedThe bearer token is missing, expired or revoked.
  • 403insufficient-scopeThe key is valid but lacks the iot.usage:read scope.
  • 404not-foundNot found, or not in your account. Resources outside your organisation are always 404, never 403.
  • 429rate-limitedRate limit exceeded. Wait for Retry-After seconds.

curl

curl "https://partners.netavo.com/api/v1/iot/sims/8944110068212345678/usage" \
  -H "Authorization: Bearer $THINGSIM_API_KEY"

Python

import os
import requests

response = requests.get(
    "https://partners.netavo.com/api/v1/iot/sims/8944110068212345678/usage",
    headers={
        "Authorization": f"Bearer {os.environ['THINGSIM_API_KEY']}",
    },
)
response.raise_for_status()
print(response.json())

Node

const response = await fetch("https://partners.netavo.com/api/v1/iot/sims/8944110068212345678/usage", {
  headers: {
    Authorization: `Bearer ${process.env.THINGSIM_API_KEY}`,
  },
});
if (!response.ok) throw new Error(await response.text());
console.log(await response.json());
Response · 200
{
  "iccid": "8944110068212345678",
  "periodStart": "2026-09-01T00:00:00Z",
  "periodEnd": "2026-10-01T00:00:00Z",
  "allowanceBytes": 1073741824,
  "usedBytes": 612368384,
  "remainingBytes": 461373440,
  "percentUsed": 57,
  "allowanceReached": false,
  "updatedAt": "2026-09-25T09:15:00Z",
  "history": [
    {
      "periodStart": "2026-08-01T00:00:00Z",
      "periodEnd": "2026-09-01T00:00:00Z",
      "allowanceBytes": 1073741824,
      "usedBytes": 803209216
    }
  ]
}
Example error · 400
400 · application/problem+json
{
  "type": "https://partners.netavo.com/api/problems/validation-failed",
  "title": "Validation failed",
  "status": 400,
  "detail": "limit must be between 1 and 100.",
  "instance": "/api/v1/iot/sims/{iccid}/usage",
  "code": "validation-failed",
  "traceId": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"
}

GET/v1/iot/usage

List fleet usage

Each SIM's data use in its current allowance period, ordered by ICCID: allowance, data used (including the session still open), whether it is online now, and — while it is stopped for having used its allowance — when it starts working again.

Scope
iot.usage:read

Parameters

  • limit integer query
    How many items to return, 1 to 100.

    Example 50

  • startingAfter string query
    Cursor from the previous page's nextCursor. Leave out for the first page.

    Example c2ltXzg5NDQxMTAwNjgyMTIzNDU2Nzg

  • iccid string query
    Only this SIM.

    Example 8944110068212345678

  • tag string query
    Only SIMs with this tag.

    Example fleet-north

  • organisationId string (uuid) query
    Partners: only this customer organisation's SIMs.

    Example 0f8b6c1e-5a2d-4c7e-9b3a-2d1e6f4a7c90

Returns 200 SimUsageSummaryList

A page of usage summaries.

Errors

  • 400validation-failedValidation failed.
  • 401unauthorizedThe bearer token is missing, expired or revoked.
  • 403insufficient-scopeThe key is valid but lacks the iot.usage:read scope.
  • 429rate-limitedRate limit exceeded. Wait for Retry-After seconds.

curl

curl "https://partners.netavo.com/api/v1/iot/usage?limit=50" \
  -H "Authorization: Bearer $THINGSIM_API_KEY"

Python

import os
import requests

response = requests.get(
    "https://partners.netavo.com/api/v1/iot/usage",
    headers={
        "Authorization": f"Bearer {os.environ['THINGSIM_API_KEY']}",
    },
    params={"limit": 50},
)
response.raise_for_status()
print(response.json())

Node

const response = await fetch("https://partners.netavo.com/api/v1/iot/usage?limit=50", {
  headers: {
    Authorization: `Bearer ${process.env.THINGSIM_API_KEY}`,
  },
});
if (!response.ok) throw new Error(await response.text());
console.log(await response.json());
Response · 200
{
  "data": [
    {
      "iccid": "8944110068212345678",
      "allowanceBytes": 1073741824,
      "usedBytes": 52428800,
      "barredUntil": null,
      "online": true
    }
  ],
  "hasMore": true,
  "nextCursor": "c2ltXzg5NDQxMTAwNjgyMTIzNDU2Nzg"
}
Example error · 400
400 · application/problem+json
{
  "type": "https://partners.netavo.com/api/problems/validation-failed",
  "title": "Validation failed",
  "status": 400,
  "detail": "limit must be between 1 and 100.",
  "instance": "/api/v1/iot/usage",
  "code": "validation-failed",
  "traceId": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"
}

eSIM

GET/v1/iot/sims/{iccid}/esim

Get an eSIM activation code

The activation code and QR code for an eSIM profile, read from the network each time. Treat the code like a password: anyone with it can download the profile until it is installed.

Scope
iot.esim:read

Parameters

  • iccid string path required
    The SIM's ICCID, 19 or 20 digits, as printed on the card.

    Example 8944110068298765432

Returns 200 EsimActivation

The activation details.

Errors

  • 401unauthorizedThe bearer token is missing, expired or revoked.
  • 403insufficient-scopeThe key is valid but lacks the iot.esim:read scope.
  • 404not-foundNot found, or not in your account. Resources outside your organisation are always 404, never 403.
  • 409not-an-esimThe SIM is a physical SIM, not an eSIM.
  • 429rate-limitedRate limit exceeded. Wait for Retry-After seconds.
  • 502carrier-errorThe network did not return an activation code. Retry later.

curl

curl "https://partners.netavo.com/api/v1/iot/sims/8944110068298765432/esim" \
  -H "Authorization: Bearer $THINGSIM_API_KEY"

Python

import os
import requests

response = requests.get(
    "https://partners.netavo.com/api/v1/iot/sims/8944110068298765432/esim",
    headers={
        "Authorization": f"Bearer {os.environ['THINGSIM_API_KEY']}",
    },
)
response.raise_for_status()
print(response.json())

Node

const response = await fetch("https://partners.netavo.com/api/v1/iot/sims/8944110068298765432/esim", {
  headers: {
    Authorization: `Bearer ${process.env.THINGSIM_API_KEY}`,
  },
});
if (!response.ok) throw new Error(await response.text());
console.log(await response.json());
Response · 200
{
  "iccid": "8944110068298765432",
  "activationCode": "LPA:1$smdp.example.net$K2-4N7Q2-1XZ9PQ",
  "smdpAddress": "smdp.example.net",
  "matchingId": "K2-4N7Q2-1XZ9PQ",
  "qrCodeSvg": "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 41 41\">…</svg>"
}
Example error · 404
404 · application/problem+json
{
  "type": "https://partners.netavo.com/api/problems/not-found",
  "title": "Not found",
  "status": 404,
  "detail": "No such resource in your account.",
  "instance": "/api/v1/iot/sims/{iccid}/esim",
  "code": "not-found",
  "traceId": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"
}

IoT schemas

The objects the IoT API sends and receives.

Coverage object

Which networks a plan can use.

  • key string required
    The coverage's id; filter plans by it with ?coverage=.

    Example 3e8d1c5a-7f2b-4a90-b6e4-1d0c9f8a2b37

  • label string required

    Example UK 4-network

  • networks string[] required
    The UK networks it permits.

    Example ["EE","O2","Three","Vodafone"]

EsimActivation object

What a device needs to download its eSIM profile.

  • iccid string required

    Example 8944110068298765432

  • activationCode string required
    The full LPA string, as encoded in the QR code.

    Example LPA:1$smdp.example.net$K2-4N7Q2-1XZ9PQ

  • smdpAddress string required

    Example smdp.example.net

  • matchingId string required

    Example K2-4N7Q2-1XZ9PQ

  • qrCodeSvg string required
    The QR code as an SVG document, ready to show or print.

    Example <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 41 41">…</svg>

FormFactor enum

Physical form of the SIM.

triSim mff2 esim

Money object

An amount of money. amount is a decimal string so it never loses pennies.

  • amount string required
    Decimal amount, two places.

    Example 2.40

  • currency string required
    ISO 4217 currency code.

    Example GBP

  • includesVat boolean required
    Whether amount includes VAT. Prices are quoted ex VAT.

    Example false

Network object

A mobile network.

  • mcc string or null
    Mobile country code.

    Example 234

  • mnc string or null
    Mobile network code.

    Example 15

  • name string or null

    Example Vodafone UK

Plan object

A plan your account can use, at your price.

  • id string required
    Pass as planId to change a SIM's plan.

    Example 5b0e4f2a-8c1d-4e6f-9a3b-7d2c1e0f4a61

  • name string required

    Example 1 GB, UK 4-network

  • coverage Coverage or null
    The networks it can use; null when the plan names none.
  • allowanceBytes integer required

    Example 1073741824

  • monthlyPrice Money required
    Your price per month, ex VAT.
  • billingIntervals BillingInterval[] required

    One of monthly annual

PlanChange object

The outcome of a plan change. Upgrades apply now and are prorated; downgrades apply at the next renewal.

  • iccid string required

    Example 8944110068212345678

  • fromPlan PlanSummary or null
    The plan before the change; null for a SIM that had none.
  • toPlan PlanSummary required
    The plan a SIM is on.
  • direction PlanChangeDirection required
    Whether a plan change is an upgrade (now) or a downgrade (at renewal).

    One of upgrade downgrade

  • effectiveAt string (date-time) required
    When the new plan takes effect: now for an upgrade, the next renewal for a downgrade.

    Example 2026-09-25T00:00:00Z

  • message string or null
    What we did, in words.

    Example Upgraded to 5 GB, UK 4-network; the difference for the rest of the period is charged now.

PlanChangeDirection enum

Whether a plan change is an upgrade (now) or a downgrade (at renewal).

upgrade downgrade

PlanChangeRequest object

The plan to move the SIM to.

  • planId string required
    A plan id from GET /v1/iot/plans.

    Example 9c7a2e14-3b5d-4f60-8e21-6a4b0d9c3f75

PlanList object

Every plan available to your account. Not paginated.

  • data Plan[] required

PlanSummary object

The plan a SIM is on.

  • id string required
    Plan id, as listed by GET /v1/iot/plans.

    Example 5b0e4f2a-8c1d-4e6f-9a3b-7d2c1e0f4a61

  • name string required

    Example 1 GB, UK 4-network

  • allowanceBytes integer required
    Data included each month, in bytes.

    Example 1073741824

Problem object

An RFC 9457 problem document. type and code are stable; title and detail are for people.

  • type string
    Identifies the kind of problem.

    Example https://partners.netavo.com/api/problems/not-found

  • title string
    Short, human-readable summary of the problem type.

    Example Not found

  • status integer
    The HTTP status code.

    Example 404

  • detail string or null
    What went wrong with this request.

    Example No SIM with ICCID 8944110068212345678 in your account.

  • instance string or null
    The request path.

    Example /api/v1/iot/sims/8944110068212345678

  • code string
    Stable machine-readable code, the last segment of type.

    Example not-found

  • traceId string or null
    Quote this if you contact support.

    Example 00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01

  • errors map of string[] or null
    Per-field messages when code is validation-failed.

ScheduledPlanChange object

A plan change that takes effect later — a downgrade waits for the next renewal.

  • plan PlanSummary required
    The plan a SIM is on.
  • effectiveAt string (date-time) required

    Example 2026-10-01T00:00:00Z

Session object

One data session: from the device attaching to the network to it detaching.

  • id string required

    Example ses_4f1c2a9e7b3d

  • iccid string required

    Example 8944110068212345678

  • startedAt string (date-time) or null

    Example 2026-09-25T06:02:41Z

  • endedAt string (date-time) or null
    Null while the session is open.

    Example 2026-09-25T08:47:12Z

  • bytesUp integer required
    Bytes sent by the device.

    Example 1843200

  • bytesDown integer required
    Bytes received by the device.

    Example 7340032

  • network Network or null
    The network the device was on, when the network reported it.
  • radioAccess string or null
    2G, 3G, 4G, 5G, LTE-M or NB-IoT, when the network reported it.

    Example 4G

  • ipAddress string or null

    Example 10.64.12.34

  • endReason string or null
    Why the session ended, when the network says.

    Example userRequest

SessionList object

A page of results. Follow nextCursor for the next one.

  • data Session[] required
    This page of results.
  • hasMore boolean required
    Whether another page follows.

    Example true

  • nextCursor string or null
    Pass as startingAfter to get the next page. Null on the last page.

    Example c2ltXzg5NDQxMTAwNjgyMTIzNDU2Nzg

Sim object

A SIM in your account.

  • iccid string required
    The SIM's ICCID. Its identifier everywhere in the API.

    Example 8944110068212345678

  • label string or null
    Your name for the SIM or the device it is in.

    Example Car park barrier 3

  • tags string[] required
    Your tags, for filtering.

    Example ["site:leeds","barrier"]

  • status SimStatus required
    Where the SIM is in its lifecycle. pending… states mean the network is still applying a change; a notification follows when it takes effect.

    One of inactive pendingActivation active pendingPause paused pendingResume suspended pendingCease ceased

  • suspendedReason SuspendedReason or null
    Set when status is paused, pendingPause or suspended.

    One of customerPause allowanceReached nonPayment provider

  • canResume boolean required
    Whether POST …/resume would succeed: the SIM is paused by you. False for every other state, including a suspension only we can lift.

    Example false

  • formFactor FormFactor required
    Physical form of the SIM.

    One of triSim mff2 esim

  • plan PlanSummary or null
    The plan the SIM is on; null for a SIM with no plan yet.
  • msisdn string or null
    The SIM's number, in international format without +.

    Example 447700900123

  • imsi string or null

    Example 234500012345678

  • ipAddress string or null
    The SIM's private IP address on the network.

    Example 10.64.12.34

  • organisationId string (uuid) required
    The organisation that owns the SIM. Partners see SIMs across their customers.

    Example 0f8b6c1e-5a2d-4c7e-9b3a-2d1e6f4a7c90

  • activatedAt string (date-time) or null
    When the SIM was first activated. Billing starts here.

    Example 2026-09-01T08:15:00Z

  • activationScheduledFor string (date-time) or null
    For a SIM awaiting activation: the day the network activates it. Activation runs on the next working day after it is requested.
  • stopsAt string (date-time) or null
    When a ceased SIM stops passing data: the end of the period already paid for. Null unless a cease is pending.
  • createdAt string (date-time) required

    Example 2026-08-20T14:02:11Z

  • updatedAt string (date-time) required

    Example 2026-09-24T17:40:03Z

  • canActivate boolean required
    Whether the SIM can be activated now: it has never been started and has a plan.

    Example false

  • planStartedAt string (date-time) or null
    When the SIM's current plan started; null before activation.

    Example 2026-09-01T00:00:00Z

  • scheduledPlanChange ScheduledPlanChange or null
    A downgrade waiting for the next renewal: the plan it moves to and when. Null when none is scheduled.

SimCeaseRequest object

Confirms which SIM to cease, because ceasing cannot be undone.

  • confirmIccid string required
    Must equal the ICCID in the path.

    Example 8944110068212345678

  • reason string or null
    Your note, kept in our logs of the SIM's history.

    Example Barrier removed

SimList object

A page of results. Follow nextCursor for the next one.

  • data Sim[] required
    This page of results.
  • hasMore boolean required
    Whether another page follows.

    Example true

  • nextCursor string or null
    Pass as startingAfter to get the next page. Null on the last page.

    Example c2ltXzg5NDQxMTAwNjgyMTIzNDU2Nzg

SimStatus enum

Where the SIM is in its lifecycle. pending… states mean the network is still applying a change; a notification follows when it takes effect.

inactive pendingActivation active pendingPause paused pendingResume suspended pendingCease ceased

SimUpdate object

Fields to change. Leave a field out (or null) to keep it.

  • label string or null
    New label; "" clears it. Null or left out keeps it (so a client that always sends every field cannot wipe a label by accident).

    Example Car park barrier 3

  • tags string[] or null
    Replaces all tags; [] removes them. Null or left out keeps them.

    Example ["site:leeds","barrier"]

SimUsage object

Data used by one SIM in one allowance period.

  • iccid string required

    Example 8944110068212345678

  • periodStart string (date-time) required

    Example 2026-09-01T00:00:00Z

  • periodEnd string (date-time) required

    Example 2026-10-01T00:00:00Z

  • allowanceBytes integer required

    Example 1073741824

  • usedBytes integer required
    For the current period, includes sessions still open.

    Example 612368384

  • remainingBytes integer required

    Example 461373440

  • percentUsed number required

    Example 57

  • allowanceReached boolean required
    True once the SIM has paused for using its whole allowance this period.

    Example false

  • updatedAt string (date-time) required
    When usage was last counted. Up to 15 minutes behind live traffic.

    Example 2026-09-25T09:15:00Z

  • history SimUsagePeriod[] required
    Earlier periods, newest first (up to twelve). Only on the current period's response.

SimUsagePeriod object

A past allowance period's totals.

  • periodStart string (date-time) required

    Example 2026-08-01T00:00:00Z

  • periodEnd string (date-time) required

    Example 2026-09-01T00:00:00Z

  • allowanceBytes integer required

    Example 1073741824

  • usedBytes integer required

    Example 803209216

SimUsageSummary object

One SIM's data use in its current allowance period, as the fleet usage list reports it.

  • iccid string required

    Example 8944110068212345678

  • allowanceBytes integer required
    The current allowance period's data allowance; 0 when the SIM has no plan.

    Example 1073741824

  • usedBytes integer required
    Data used so far this period, including the session still open.

    Example 52428800

  • barredUntil string (date-time) or null
    Set while the SIM is stopped for having used its allowance: when it starts working again (the allowance resets). Null when it is not barred.
  • online boolean required
    Whether the SIM has a data session open now.

    Example true

SimUsageSummaryList object

A page of results. Follow nextCursor for the next one.

  • data SimUsageSummary[] required
    This page of results.
  • hasMore boolean required
    Whether another page follows.

    Example true

  • nextCursor string or null
    Pass as startingAfter to get the next page. Null on the last page.

    Example c2ltXzg5NDQxMTAwNjgyMTIzNDU2Nzg

SuspendedReason enum

Why a paused or suspended SIM is not passing data. Only customerPause can be resumed through the API.

customerPause allowanceReached nonPayment provider

Platform API · v1

Platform

Account-wide features: branding, webhooks, events and API keys.

ApiKeys

GET/v1/platform/api-keys

List API keys

Every API key of your organisation, newest first, active or not. Secrets are never returned.

Scope
platform.keys:read

Returns 200 ApiKeyList

The keys.

Errors

  • 401unauthorizedThe bearer token is missing, expired or revoked.
  • 403insufficient-scopeAPI keys are managed with an API key that has explicit scopes.
  • 429rate-limitedRate limit exceeded. Wait for Retry-After seconds.

curl

curl "https://partners.netavo.com/api/v1/platform/api-keys" \
  -H "Authorization: Bearer $THINGSIM_API_KEY"

Python

import os
import requests

response = requests.get(
    "https://partners.netavo.com/api/v1/platform/api-keys",
    headers={
        "Authorization": f"Bearer {os.environ['THINGSIM_API_KEY']}",
    },
)
response.raise_for_status()
print(response.json())

Node

const response = await fetch("https://partners.netavo.com/api/v1/platform/api-keys", {
  headers: {
    Authorization: `Bearer ${process.env.THINGSIM_API_KEY}`,
  },
});
if (!response.ok) throw new Error(await response.text());
console.log(await response.json());
Response · 200
{
  "data": [
    {
      "id": "key_0192a4c3e5f67b8d9e0f1a2b3c4d5e6f",
      "name": "Fleet dashboard",
      "scopes": [
        "iot.sims:read",
        "iot.usage:read"
      ],
      "status": "active",
      "allowedIpRanges": [
        "203.0.113.0/24"
      ],
      "createdAt": "2026-09-25T10:00:00Z",
      "expiresAt": "2027-09-25T10:00:00Z",
      "lastUsedAt": "2026-09-25T11:30:00Z",
      "revokedAt": null
    }
  ]
}

POST/v1/platform/api-keys

Create an API key

Creates a key for your organisation. It can have only scopes the calling key holds, cannot outlive it, and inherits its address limits (which it may narrow). The secret is in this response only — store it now.

Scope
platform.keys:manage
Idempotent
Send an Idempotency-Key header

Request body ApiKeyCreate

  • name string required
    What the key is for, so you can recognise it later.

    Example Fleet dashboard

  • scopes string[] required
    The scopes the key may use. Each must be one the calling credential holds itself: a key can never do more than the key that created it.

    Example ["iot.sims:read","iot.usage:read"]

  • expiresAt string (date-time) or null
    When the key stops working, at most a year ahead (and no later than the calling key's own expiry). Leave out for a year.

    Example 2027-03-31T00:00:00Z

  • allowedIpRanges string[] or null
    Limit the key to these source addresses or CIDR ranges. Leave out for anywhere — unless the calling key is itself limited, in which case the new key inherits its limits and may only narrow them.

    Example ["203.0.113.0/24"]

Returns 201 ApiKeyCreated

The key and its secret.

Errors

  • 400validation-failedValidation failed.
  • 401unauthorizedThe bearer token is missing, expired or revoked.
  • 403insufficient-scopeThe key would have more access than the key creating it.
  • 422idempotency-key-reusedThe Idempotency-Key was already used for a different request.
  • 429rate-limitedRate limit exceeded. Wait for Retry-After seconds.

curl

curl -X POST "https://partners.netavo.com/api/v1/platform/api-keys" \
  -H "Authorization: Bearer $THINGSIM_API_KEY" \
  -H "Idempotency-Key: 5d1c7a0e-2f4b-4e8a-9c61-3b7f0d2e8a14" \
  -H "Content-Type: application/json" \
  -d '{
  "name": "Fleet dashboard",
  "scopes": [
    "iot.sims:read",
    "iot.usage:read"
  ],
  "expiresAt": "2027-03-31T00:00:00Z",
  "allowedIpRanges": [
    "203.0.113.0/24"
  ]
}'

Python

import os
import requests

response = requests.post(
    "https://partners.netavo.com/api/v1/platform/api-keys",
    headers={
        "Authorization": f"Bearer {os.environ['THINGSIM_API_KEY']}",
        "Idempotency-Key": "5d1c7a0e-2f4b-4e8a-9c61-3b7f0d2e8a14",
    },
    json={
        "name": "Fleet dashboard",
        "scopes": ["iot.sims:read", "iot.usage:read"],
        "expiresAt": "2027-03-31T00:00:00Z",
        "allowedIpRanges": ["203.0.113.0/24"],
    },
)
response.raise_for_status()
print(response.json())

Node

const response = await fetch("https://partners.netavo.com/api/v1/platform/api-keys", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.THINGSIM_API_KEY}`,
    "Idempotency-Key": "5d1c7a0e-2f4b-4e8a-9c61-3b7f0d2e8a14",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "name": "Fleet dashboard",
    "scopes": [
      "iot.sims:read",
      "iot.usage:read"
    ],
    "expiresAt": "2027-03-31T00:00:00Z",
    "allowedIpRanges": [
      "203.0.113.0/24"
    ]
  }),
});
if (!response.ok) throw new Error(await response.text());
console.log(await response.json());
Response · 201
{
  "apiKey": {
    "id": "key_0192a4c3e5f67b8d9e0f1a2b3c4d5e6f",
    "name": "Fleet dashboard",
    "scopes": [
      "iot.sims:read",
      "iot.usage:read"
    ],
    "status": "active",
    "allowedIpRanges": [
      "203.0.113.0/24"
    ],
    "createdAt": "2026-09-25T10:00:00Z",
    "expiresAt": "2027-09-25T10:00:00Z",
    "lastUsedAt": "2026-09-25T11:30:00Z",
    "revokedAt": null
  },
  "secret": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.…"
}
Example error · 400
400 · application/problem+json
{
  "type": "https://partners.netavo.com/api/problems/validation-failed",
  "title": "Validation failed",
  "status": 400,
  "detail": "limit must be between 1 and 100.",
  "instance": "/api/v1/platform/api-keys",
  "code": "validation-failed",
  "traceId": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"
}

DELETE/v1/platform/api-keys/{keyId}

Revoke an API key

Stops the key working at once. Revoking a key that is already revoked succeeds.

Scope
platform.keys:manage

Parameters

  • keyId string path required
    The key's id.

    Example key_0192a4c3e5f67b8d9e0f1a2b3c4d5e6f

Returns 204

Revoked.

Errors

  • 401unauthorizedThe bearer token is missing, expired or revoked.
  • 403insufficient-scopeThe key is valid but lacks the platform.keys:manage scope.
  • 404not-foundNot found, or not in your account. Resources outside your organisation are always 404, never 403.
  • 429rate-limitedRate limit exceeded. Wait for Retry-After seconds.

curl

curl -X DELETE "https://partners.netavo.com/api/v1/platform/api-keys/key_0192a4c3e5f67b8d9e0f1a2b3c4d5e6f" \
  -H "Authorization: Bearer $THINGSIM_API_KEY"

Python

import os
import requests

response = requests.delete(
    "https://partners.netavo.com/api/v1/platform/api-keys/key_0192a4c3e5f67b8d9e0f1a2b3c4d5e6f",
    headers={
        "Authorization": f"Bearer {os.environ['THINGSIM_API_KEY']}",
    },
)
response.raise_for_status()
print(response.status_code)

Node

const response = await fetch("https://partners.netavo.com/api/v1/platform/api-keys/key_0192a4c3e5f67b8d9e0f1a2b3c4d5e6f", {
  method: "DELETE",
  headers: {
    Authorization: `Bearer ${process.env.THINGSIM_API_KEY}`,
  },
});
if (!response.ok) throw new Error(await response.text());
console.log(response.status);

204: no body.

Example error · 404
404 · application/problem+json
{
  "type": "https://partners.netavo.com/api/problems/not-found",
  "title": "Not found",
  "status": 404,
  "detail": "No such resource in your account.",
  "instance": "/api/v1/platform/api-keys/{keyId}",
  "code": "not-found",
  "traceId": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"
}

Branding

GET/v1/platform/branding

Get branding information for the authenticated user or extension Walks up the organisation hierarchy to find custom branding

Scope
platform.branding:read

Returns 200 BrandingResponseApiResponse

Branding information found and returned

Errors

  • 401Not authenticated or invalid token
  • 403insufficient-scopeThe key is valid but lacks the platform.branding:read scope.
  • 404No custom branding found for this organisation hierarchy
  • 429rate-limitedRate limit exceeded. Wait for Retry-After seconds.

curl

curl "https://partners.netavo.com/api/v1/platform/branding" \
  -H "Authorization: Bearer $THINGSIM_API_KEY"

Python

import os
import requests

response = requests.get(
    "https://partners.netavo.com/api/v1/platform/branding",
    headers={
        "Authorization": f"Bearer {os.environ['THINGSIM_API_KEY']}",
    },
)
response.raise_for_status()
print(response.json())

Node

const response = await fetch("https://partners.netavo.com/api/v1/platform/branding", {
  headers: {
    Authorization: `Bearer ${process.env.THINGSIM_API_KEY}`,
  },
});
if (!response.ok) throw new Error(await response.text());
console.log(await response.json());
Response · 200
{
  "success": false,
  "data": {
    "organisationId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "organisationName": "string",
    "primaryColor": "string",
    "secondaryColor": "string",
    "supportPhone": "string",
    "supportEmail": "string",
    "logoSvg": "string",
    "isInherited": false
  },
  "message": "string"
}
Example error · 404
404 · application/json
{
  "success": false,
  "error": {
    "code": "string",
    "message": "string",
    "details": {}
  }
}

Events

GET/v1/platform/events

List events

Notifications raised for your account in the last 30 days, newest first — for partners, those of your customer organisations too. Use it to catch up after downtime.

Scope
platform.events:read

Parameters

  • limit integer query
    How many items to return, 1 to 100.

    Example 50

  • startingAfter string query
    Cursor from the previous page's nextCursor. Leave out for the first page.

    Example c2ltXzg5NDQxMTAwNjgyMTIzNDU2Nzg

  • type string query
    Only events of this type.

    Example iot.sim.allowance_reached

  • since string (date-time) query
    Only events at or after this time.

    Example 2026-09-24T00:00:00Z

Returns 200 EventList

A page of events.

Errors

  • 400validation-failedValidation failed.
  • 401unauthorizedThe bearer token is missing, expired or revoked.
  • 403insufficient-scopeThe key is valid but lacks the platform.events:read scope.
  • 429rate-limitedRate limit exceeded. Wait for Retry-After seconds.

curl

curl "https://partners.netavo.com/api/v1/platform/events?limit=50" \
  -H "Authorization: Bearer $THINGSIM_API_KEY"

Python

import os
import requests

response = requests.get(
    "https://partners.netavo.com/api/v1/platform/events",
    headers={
        "Authorization": f"Bearer {os.environ['THINGSIM_API_KEY']}",
    },
    params={"limit": 50},
)
response.raise_for_status()
print(response.json())

Node

const response = await fetch("https://partners.netavo.com/api/v1/platform/events?limit=50", {
  headers: {
    Authorization: `Bearer ${process.env.THINGSIM_API_KEY}`,
  },
});
if (!response.ok) throw new Error(await response.text());
console.log(await response.json());
Response · 200
{
  "data": [
    {
      "id": "evt_0192a4c3e5f67b8d9e0f1a2b3c4d5e72",
      "type": "iot.sim.allowance_reached",
      "serviceArea": "iot",
      "organisationId": "0f8b6c1e-5a2d-4c7e-9b3a-2d1e6f4a7c90",
      "occurredAt": "2026-09-25T09:12:44Z",
      "data": {
        "iccid": "8944110068212345678",
        "label": "Car park barrier 3",
        "planId": "5b0e4f2a-8c1d-4e6f-9a3b-7d2c1e0f4a61",
        "allowanceBytes": 1073741824,
        "usedBytes": 1073741824,
        "periodEnd": "2026-10-01T00:00:00Z"
      }
    }
  ],
  "hasMore": false,
  "nextCursor": null
}
Example error · 400
400 · application/problem+json
{
  "type": "https://partners.netavo.com/api/problems/validation-failed",
  "title": "Validation failed",
  "status": 400,
  "detail": "limit must be between 1 and 100.",
  "instance": "/api/v1/platform/events",
  "code": "validation-failed",
  "traceId": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"
}

GET/v1/platform/events/{eventId}

Get an event

One event by id, exactly as it was delivered.

Scope
platform.events:read

Parameters

  • eventId string path required
    The event's id.

    Example evt_0192a4c3e5f67b8d9e0f1a2b3c4d5e72

Returns 200 WebhookEvent

The event.

Errors

  • 401unauthorizedThe bearer token is missing, expired or revoked.
  • 403insufficient-scopeThe key is valid but lacks the platform.events:read scope.
  • 404not-foundNot found, or not in your account. Resources outside your organisation are always 404, never 403.
  • 429rate-limitedRate limit exceeded. Wait for Retry-After seconds.

curl

curl "https://partners.netavo.com/api/v1/platform/events/evt_0192a4c3e5f67b8d9e0f1a2b3c4d5e72" \
  -H "Authorization: Bearer $THINGSIM_API_KEY"

Python

import os
import requests

response = requests.get(
    "https://partners.netavo.com/api/v1/platform/events/evt_0192a4c3e5f67b8d9e0f1a2b3c4d5e72",
    headers={
        "Authorization": f"Bearer {os.environ['THINGSIM_API_KEY']}",
    },
)
response.raise_for_status()
print(response.json())

Node

const response = await fetch("https://partners.netavo.com/api/v1/platform/events/evt_0192a4c3e5f67b8d9e0f1a2b3c4d5e72", {
  headers: {
    Authorization: `Bearer ${process.env.THINGSIM_API_KEY}`,
  },
});
if (!response.ok) throw new Error(await response.text());
console.log(await response.json());
Response · 200
{
  "id": "evt_0192a4c3e5f67b8d9e0f1a2b3c4d5e72",
  "type": "iot.sim.allowance_reached",
  "serviceArea": "iot",
  "organisationId": "0f8b6c1e-5a2d-4c7e-9b3a-2d1e6f4a7c90",
  "occurredAt": "2026-09-25T09:12:44Z",
  "data": {
    "iccid": "8944110068212345678",
    "label": "Car park barrier 3",
    "planId": "5b0e4f2a-8c1d-4e6f-9a3b-7d2c1e0f4a61",
    "allowanceBytes": 1073741824,
    "usedBytes": 1073741824,
    "periodEnd": "2026-10-01T00:00:00Z"
  }
}
Example error · 404
404 · application/problem+json
{
  "type": "https://partners.netavo.com/api/problems/not-found",
  "title": "Not found",
  "status": 404,
  "detail": "No such resource in your account.",
  "instance": "/api/v1/platform/events/{eventId}",
  "code": "not-found",
  "traceId": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"
}

GET/v1/platform/notification-types

List notification types

The notification catalogue, filtered to your account's service areas. available: false marks a type that is catalogued but not yet sent; you can subscribe to it now.

Scope
platform.events:read

Parameters

  • serviceArea string query
    Only this service area: iot, connectivity, voice, hosting or platform.

    Example iot

Returns 200 NotificationTypeList

The catalogue.

Errors

  • 400validation-failedValidation failed.
  • 401unauthorizedThe bearer token is missing, expired or revoked.
  • 403insufficient-scopeThe key is valid but lacks the platform.events:read scope.
  • 429rate-limitedRate limit exceeded. Wait for Retry-After seconds.

curl

curl "https://partners.netavo.com/api/v1/platform/notification-types" \
  -H "Authorization: Bearer $THINGSIM_API_KEY"

Python

import os
import requests

response = requests.get(
    "https://partners.netavo.com/api/v1/platform/notification-types",
    headers={
        "Authorization": f"Bearer {os.environ['THINGSIM_API_KEY']}",
    },
)
response.raise_for_status()
print(response.json())

Node

const response = await fetch("https://partners.netavo.com/api/v1/platform/notification-types", {
  headers: {
    Authorization: `Bearer ${process.env.THINGSIM_API_KEY}`,
  },
});
if (!response.ok) throw new Error(await response.text());
console.log(await response.json());
Response · 200
{
  "data": [
    {
      "type": "iot.sim.allowance_reached",
      "serviceArea": "iot",
      "description": "A SIM used its whole data allowance for the period and has stopped passing data.",
      "optIn": false,
      "available": true,
      "channels": [
        "webhook"
      ],
      "examplePayload": {}
    }
  ]
}
Example error · 400
400 · application/problem+json
{
  "type": "https://partners.netavo.com/api/problems/validation-failed",
  "title": "Validation failed",
  "status": 400,
  "detail": "limit must be between 1 and 100.",
  "instance": "/api/v1/platform/notification-types",
  "code": "validation-failed",
  "traceId": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"
}

Webhooks

GET/v1/platform/webhooks

List webhooks

Every webhook endpoint on your account, newest first — for partners, those of your customer organisations too.

Scope
platform.webhooks:read

Parameters

  • limit integer query
    How many items to return, 1 to 100.

    Example 50

  • startingAfter string query
    Cursor from the previous page's nextCursor. Leave out for the first page.

    Example c2ltXzg5NDQxMTAwNjgyMTIzNDU2Nzg

Returns 200 WebhookList

A page of webhooks.

Errors

  • 400validation-failedValidation failed.
  • 401unauthorizedThe bearer token is missing, expired or revoked.
  • 403insufficient-scopeThe key is valid but lacks the platform.webhooks:read scope.
  • 429rate-limitedRate limit exceeded. Wait for Retry-After seconds.

curl

curl "https://partners.netavo.com/api/v1/platform/webhooks?limit=50" \
  -H "Authorization: Bearer $THINGSIM_API_KEY"

Python

import os
import requests

response = requests.get(
    "https://partners.netavo.com/api/v1/platform/webhooks",
    headers={
        "Authorization": f"Bearer {os.environ['THINGSIM_API_KEY']}",
    },
    params={"limit": 50},
)
response.raise_for_status()
print(response.json())

Node

const response = await fetch("https://partners.netavo.com/api/v1/platform/webhooks?limit=50", {
  headers: {
    Authorization: `Bearer ${process.env.THINGSIM_API_KEY}`,
  },
});
if (!response.ok) throw new Error(await response.text());
console.log(await response.json());
Response · 200
{
  "data": [
    {
      "id": "wh_0192a4c3e5f67b8d9e0f1a2b3c4d5e6f",
      "organisationId": "0f8b6c1e-5a2d-4c7e-9b3a-2d1e6f4a7c90",
      "url": "https://example.com/hooks/thingsim",
      "description": "Ops alerts",
      "types": [
        "iot.sim.allowance_reached",
        "iot.sim.usage_threshold"
      ],
      "serviceAreas": [
        "voice"
      ],
      "includeSubtree": false,
      "enabled": true,
      "status": "active",
      "disabledReason": null,
      "createdAt": "2026-09-20T10:00:00Z",
      "updatedAt": "2026-09-20T10:00:00Z"
    }
  ],
  "hasMore": false,
  "nextCursor": null
}
Example error · 400
400 · application/problem+json
{
  "type": "https://partners.netavo.com/api/problems/validation-failed",
  "title": "Validation failed",
  "status": 400,
  "detail": "limit must be between 1 and 100.",
  "instance": "/api/v1/platform/webhooks",
  "code": "validation-failed",
  "traceId": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"
}

POST/v1/platform/webhooks

Create a webhook

Adds an endpoint. The response holds the signing secret, which is not shown again. The URL must be HTTPS on a public address. Send a test event afterwards to check your endpoint. Partners can create one for a customer organisation with organisationId.

Scope
platform.webhooks:manage
Idempotent
Send an Idempotency-Key header

Request body WebhookCreate

  • url string required
    Where we POST events. HTTPS on a public address; redirects are not followed.

    Example https://example.com/hooks/thingsim

  • description string or null

    Example Ops alerts

  • types string[] or null
    Notification types to send. Leave out (with serviceAreas) for every type your account can receive, except opt-in types.

    Example ["iot.sim.allowance_reached","iot.sim.usage_threshold"]

  • serviceAreas ServiceArea[] or null
    Send every type in these service areas.

    One of voice connectivity hosting iot platform

  • includeSubtree boolean or null
    Partners: also send events for customer organisations below you. Null or left out is false.

    Example false

  • organisationId string (uuid) or null
    Partners: create the webhook for a customer organisation below you. Defaults to your own organisation.

Returns 201 WebhookCreated

The webhook and its signing secret.

Errors

  • 400validation-failedValidation failed.
  • 401unauthorizedThe bearer token is missing, expired or revoked.
  • 403insufficient-scopeThe key is valid but lacks the platform.webhooks:manage scope.
  • 404not-foundorganisationId is not an organisation in your account.
  • 409invalid-stateThe organisation already has the most webhooks it may have (20).
  • 422idempotency-key-reusedThe Idempotency-Key was already used for a different request.
  • 429rate-limitedRate limit exceeded. Wait for Retry-After seconds.

curl

curl -X POST "https://partners.netavo.com/api/v1/platform/webhooks" \
  -H "Authorization: Bearer $THINGSIM_API_KEY" \
  -H "Idempotency-Key: 5d1c7a0e-2f4b-4e8a-9c61-3b7f0d2e8a14" \
  -H "Content-Type: application/json" \
  -d '{
  "url": "https://example.com/hooks/thingsim",
  "description": "Ops alerts",
  "types": [
    "iot.sim.allowance_reached",
    "iot.sim.usage_threshold"
  ],
  "serviceAreas": [
    "voice"
  ],
  "includeSubtree": false,
  "organisationId": null
}'

Python

import os
import requests

response = requests.post(
    "https://partners.netavo.com/api/v1/platform/webhooks",
    headers={
        "Authorization": f"Bearer {os.environ['THINGSIM_API_KEY']}",
        "Idempotency-Key": "5d1c7a0e-2f4b-4e8a-9c61-3b7f0d2e8a14",
    },
    json={
        "url": "https://example.com/hooks/thingsim",
        "description": "Ops alerts",
        "types": ["iot.sim.allowance_reached", "iot.sim.usage_threshold"],
        "serviceAreas": ["voice"],
        "includeSubtree": False,
        "organisationId": None,
    },
)
response.raise_for_status()
print(response.json())

Node

const response = await fetch("https://partners.netavo.com/api/v1/platform/webhooks", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.THINGSIM_API_KEY}`,
    "Idempotency-Key": "5d1c7a0e-2f4b-4e8a-9c61-3b7f0d2e8a14",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "url": "https://example.com/hooks/thingsim",
    "description": "Ops alerts",
    "types": [
      "iot.sim.allowance_reached",
      "iot.sim.usage_threshold"
    ],
    "serviceAreas": [
      "voice"
    ],
    "includeSubtree": false,
    "organisationId": null
  }),
});
if (!response.ok) throw new Error(await response.text());
console.log(await response.json());
Response · 201
{
  "webhook": {
    "id": "wh_0192a4c3e5f67b8d9e0f1a2b3c4d5e6f",
    "organisationId": "0f8b6c1e-5a2d-4c7e-9b3a-2d1e6f4a7c90",
    "url": "https://example.com/hooks/thingsim",
    "description": "Ops alerts",
    "types": [
      "iot.sim.allowance_reached",
      "iot.sim.usage_threshold"
    ],
    "serviceAreas": [
      "voice"
    ],
    "includeSubtree": false,
    "enabled": true,
    "status": "active",
    "disabledReason": null,
    "createdAt": "2026-09-20T10:00:00Z",
    "updatedAt": "2026-09-20T10:00:00Z"
  },
  "signingSecret": "whsec_3yJ8mQ2vN6pR9tW1zC4fH7kL0sX5bE8g"
}
Example error · 400
400 · application/problem+json
{
  "type": "https://partners.netavo.com/api/problems/validation-failed",
  "title": "Validation failed",
  "status": 400,
  "detail": "limit must be between 1 and 100.",
  "instance": "/api/v1/platform/webhooks",
  "code": "validation-failed",
  "traceId": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"
}

GET/v1/platform/webhooks/{webhookId}

Get a webhook

One webhook endpoint. The signing secret is never returned here.

Scope
platform.webhooks:read

Parameters

  • webhookId string path required
    The webhook's id.

    Example wh_0192a4c3e5f67b8d9e0f1a2b3c4d5e6f

Returns 200 Webhook

The webhook.

Errors

  • 401unauthorizedThe bearer token is missing, expired or revoked.
  • 403insufficient-scopeThe key is valid but lacks the platform.webhooks:read scope.
  • 404not-foundNot found, or not in your account. Resources outside your organisation are always 404, never 403.
  • 429rate-limitedRate limit exceeded. Wait for Retry-After seconds.

curl

curl "https://partners.netavo.com/api/v1/platform/webhooks/wh_0192a4c3e5f67b8d9e0f1a2b3c4d5e6f" \
  -H "Authorization: Bearer $THINGSIM_API_KEY"

Python

import os
import requests

response = requests.get(
    "https://partners.netavo.com/api/v1/platform/webhooks/wh_0192a4c3e5f67b8d9e0f1a2b3c4d5e6f",
    headers={
        "Authorization": f"Bearer {os.environ['THINGSIM_API_KEY']}",
    },
)
response.raise_for_status()
print(response.json())

Node

const response = await fetch("https://partners.netavo.com/api/v1/platform/webhooks/wh_0192a4c3e5f67b8d9e0f1a2b3c4d5e6f", {
  headers: {
    Authorization: `Bearer ${process.env.THINGSIM_API_KEY}`,
  },
});
if (!response.ok) throw new Error(await response.text());
console.log(await response.json());
Response · 200
{
  "id": "wh_0192a4c3e5f67b8d9e0f1a2b3c4d5e6f",
  "organisationId": "0f8b6c1e-5a2d-4c7e-9b3a-2d1e6f4a7c90",
  "url": "https://example.com/hooks/thingsim",
  "description": "Ops alerts",
  "types": [
    "iot.sim.allowance_reached",
    "iot.sim.usage_threshold"
  ],
  "serviceAreas": [
    "voice"
  ],
  "includeSubtree": false,
  "enabled": true,
  "status": "active",
  "disabledReason": null,
  "createdAt": "2026-09-20T10:00:00Z",
  "updatedAt": "2026-09-20T10:00:00Z"
}
Example error · 404
404 · application/problem+json
{
  "type": "https://partners.netavo.com/api/problems/not-found",
  "title": "Not found",
  "status": 404,
  "detail": "No such resource in your account.",
  "instance": "/api/v1/platform/webhooks/{webhookId}",
  "code": "not-found",
  "traceId": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"
}

PATCH/v1/platform/webhooks/{webhookId}

Update a webhook

Changes the URL, the types it receives, or turns it on and off. Leave a field out to keep it. Re-enabling a disabled webhook resets its failure count.

Scope
platform.webhooks:manage

Parameters

  • webhookId string path required
    The webhook's id.

    Example wh_0192a4c3e5f67b8d9e0f1a2b3c4d5e6f

Request body WebhookUpdate

  • url string or null
    Where we POST events. HTTPS on a public address; redirects are not followed.

    Example https://example.com/hooks/thingsim

  • description string or null

    Example Ops alerts

  • types string[] or null
    Replaces the types sent.

    Example ["iot.sim.allowance_reached"]

  • serviceAreas ServiceArea[] or null
    Replaces the service areas sent.

    One of voice connectivity hosting iot platform

  • includeSubtree boolean or null

    Example false

  • enabled boolean or null
    Re-enabling a disabled webhook resets its failure count.

    Example true

Returns 200 Webhook

The updated webhook.

Errors

  • 400validation-failedValidation failed.
  • 401unauthorizedThe bearer token is missing, expired or revoked.
  • 403insufficient-scopeThe key is valid but lacks the platform.webhooks:manage scope.
  • 404not-foundNot found, or not in your account. Resources outside your organisation are always 404, never 403.
  • 429rate-limitedRate limit exceeded. Wait for Retry-After seconds.

curl

curl -X PATCH "https://partners.netavo.com/api/v1/platform/webhooks/wh_0192a4c3e5f67b8d9e0f1a2b3c4d5e6f" \
  -H "Authorization: Bearer $THINGSIM_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "url": "https://example.com/hooks/thingsim",
  "description": "Ops alerts",
  "types": [
    "iot.sim.allowance_reached"
  ],
  "serviceAreas": [
    "voice"
  ],
  "includeSubtree": false,
  "enabled": true
}'

Python

import os
import requests

response = requests.patch(
    "https://partners.netavo.com/api/v1/platform/webhooks/wh_0192a4c3e5f67b8d9e0f1a2b3c4d5e6f",
    headers={
        "Authorization": f"Bearer {os.environ['THINGSIM_API_KEY']}",
    },
    json={
        "url": "https://example.com/hooks/thingsim",
        "description": "Ops alerts",
        "types": ["iot.sim.allowance_reached"],
        "serviceAreas": ["voice"],
        "includeSubtree": False,
        "enabled": True,
    },
)
response.raise_for_status()
print(response.json())

Node

const response = await fetch("https://partners.netavo.com/api/v1/platform/webhooks/wh_0192a4c3e5f67b8d9e0f1a2b3c4d5e6f", {
  method: "PATCH",
  headers: {
    Authorization: `Bearer ${process.env.THINGSIM_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "url": "https://example.com/hooks/thingsim",
    "description": "Ops alerts",
    "types": [
      "iot.sim.allowance_reached"
    ],
    "serviceAreas": [
      "voice"
    ],
    "includeSubtree": false,
    "enabled": true
  }),
});
if (!response.ok) throw new Error(await response.text());
console.log(await response.json());
Response · 200
{
  "id": "wh_0192a4c3e5f67b8d9e0f1a2b3c4d5e6f",
  "organisationId": "0f8b6c1e-5a2d-4c7e-9b3a-2d1e6f4a7c90",
  "url": "https://example.com/hooks/thingsim",
  "description": "Ops alerts",
  "types": [
    "iot.sim.allowance_reached",
    "iot.sim.usage_threshold"
  ],
  "serviceAreas": [
    "voice"
  ],
  "includeSubtree": false,
  "enabled": true,
  "status": "active",
  "disabledReason": null,
  "createdAt": "2026-09-20T10:00:00Z",
  "updatedAt": "2026-09-20T10:00:00Z"
}
Example error · 400
400 · application/problem+json
{
  "type": "https://partners.netavo.com/api/problems/validation-failed",
  "title": "Validation failed",
  "status": 400,
  "detail": "limit must be between 1 and 100.",
  "instance": "/api/v1/platform/webhooks/{webhookId}",
  "code": "validation-failed",
  "traceId": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"
}

DELETE/v1/platform/webhooks/{webhookId}

Delete a webhook

Removes the endpoint and its delivery log. Deliveries in flight are abandoned.

Scope
platform.webhooks:manage

Parameters

  • webhookId string path required
    The webhook's id.

    Example wh_0192a4c3e5f67b8d9e0f1a2b3c4d5e6f

Returns 204

Deleted.

Errors

  • 401unauthorizedThe bearer token is missing, expired or revoked.
  • 403insufficient-scopeThe key is valid but lacks the platform.webhooks:manage scope.
  • 404not-foundNot found, or not in your account. Resources outside your organisation are always 404, never 403.
  • 429rate-limitedRate limit exceeded. Wait for Retry-After seconds.

curl

curl -X DELETE "https://partners.netavo.com/api/v1/platform/webhooks/wh_0192a4c3e5f67b8d9e0f1a2b3c4d5e6f" \
  -H "Authorization: Bearer $THINGSIM_API_KEY"

Python

import os
import requests

response = requests.delete(
    "https://partners.netavo.com/api/v1/platform/webhooks/wh_0192a4c3e5f67b8d9e0f1a2b3c4d5e6f",
    headers={
        "Authorization": f"Bearer {os.environ['THINGSIM_API_KEY']}",
    },
)
response.raise_for_status()
print(response.status_code)

Node

const response = await fetch("https://partners.netavo.com/api/v1/platform/webhooks/wh_0192a4c3e5f67b8d9e0f1a2b3c4d5e6f", {
  method: "DELETE",
  headers: {
    Authorization: `Bearer ${process.env.THINGSIM_API_KEY}`,
  },
});
if (!response.ok) throw new Error(await response.text());
console.log(response.status);

204: no body.

Example error · 404
404 · application/problem+json
{
  "type": "https://partners.netavo.com/api/problems/not-found",
  "title": "Not found",
  "status": 404,
  "detail": "No such resource in your account.",
  "instance": "/api/v1/platform/webhooks/{webhookId}",
  "code": "not-found",
  "traceId": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"
}

GET/v1/platform/webhooks/{webhookId}/deliveries

List deliveries

Delivery attempts to this endpoint in the last 30 days, newest first. Failed deliveries are retried with back-off for about 24 hours.

Scope
platform.webhooks:read

Parameters

  • webhookId string path required
    The webhook's id.

    Example wh_0192a4c3e5f67b8d9e0f1a2b3c4d5e6f

  • limit integer query
    How many items to return, 1 to 100.

    Example 50

  • startingAfter string query
    Cursor from the previous page's nextCursor. Leave out for the first page.

    Example c2ltXzg5NDQxMTAwNjgyMTIzNDU2Nzg

  • outcome string query
    Only attempts with this outcome: succeeded, failed or pending.

    Example failed

Returns 200 WebhookDeliveryList

A page of deliveries.

Errors

  • 400validation-failedValidation failed.
  • 401unauthorizedThe bearer token is missing, expired or revoked.
  • 403insufficient-scopeThe key is valid but lacks the platform.webhooks:read scope.
  • 404not-foundNot found, or not in your account. Resources outside your organisation are always 404, never 403.
  • 429rate-limitedRate limit exceeded. Wait for Retry-After seconds.

curl

curl "https://partners.netavo.com/api/v1/platform/webhooks/wh_0192a4c3e5f67b8d9e0f1a2b3c4d5e6f/deliveries?limit=50" \
  -H "Authorization: Bearer $THINGSIM_API_KEY"

Python

import os
import requests

response = requests.get(
    "https://partners.netavo.com/api/v1/platform/webhooks/wh_0192a4c3e5f67b8d9e0f1a2b3c4d5e6f/deliveries",
    headers={
        "Authorization": f"Bearer {os.environ['THINGSIM_API_KEY']}",
    },
    params={"limit": 50},
)
response.raise_for_status()
print(response.json())

Node

const response = await fetch("https://partners.netavo.com/api/v1/platform/webhooks/wh_0192a4c3e5f67b8d9e0f1a2b3c4d5e6f/deliveries?limit=50", {
  headers: {
    Authorization: `Bearer ${process.env.THINGSIM_API_KEY}`,
  },
});
if (!response.ok) throw new Error(await response.text());
console.log(await response.json());
Response · 200
{
  "data": [
    {
      "id": "dlv_0192a4c3e5f67b8d9e0f1a2b3c4d5e71",
      "eventId": "evt_0192a4c3e5f67b8d9e0f1a2b3c4d5e72",
      "eventType": "iot.sim.allowance_reached",
      "attempt": 1,
      "outcome": "succeeded",
      "responseStatus": 200,
      "durationMs": 184,
      "error": null,
      "attemptedAt": "2026-09-25T09:12:45Z",
      "nextAttemptAt": null
    }
  ],
  "hasMore": false,
  "nextCursor": null
}
Example error · 400
400 · application/problem+json
{
  "type": "https://partners.netavo.com/api/problems/validation-failed",
  "title": "Validation failed",
  "status": 400,
  "detail": "limit must be between 1 and 100.",
  "instance": "/api/v1/platform/webhooks/{webhookId}/deliveries",
  "code": "validation-failed",
  "traceId": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"
}

POST/v1/platform/webhooks/{webhookId}/deliveries/{deliveryId}/redeliver

Redeliver an event

Sends the event of a delivery attempt to the webhook again, now, whatever its outcome was. Refused while the webhook is switched off or the delivery already has a retry pending.

Scope
platform.webhooks:manage
Idempotent
Send an Idempotency-Key header

Parameters

  • webhookId string path required
    The webhook's id.

    Example wh_0192a4c3e5f67b8d9e0f1a2b3c4d5e6f

  • deliveryId string path required
    A delivery attempt's id, from the delivery log.

    Example dlv_0192a4c3e5f67b8d9e0f1a2b3c4d5e71

Returns 202 WebhookDelivery

Queued. The new attempt appears in the delivery log.

Errors

  • 401unauthorizedThe bearer token is missing, expired or revoked.
  • 403insufficient-scopeThe key is valid but lacks the platform.webhooks:manage scope.
  • 404not-foundNot found, or not in your account. Resources outside your organisation are always 404, never 403.
  • 409invalid-stateThe webhook is switched off, or the delivery is already queued.
  • 422idempotency-key-reusedThe Idempotency-Key was already used for a different request.
  • 429rate-limitedRate limit exceeded. Wait for Retry-After seconds.
  • 503service-unavailableDeliveries cannot be queued right now. Retry shortly.

curl

curl -X POST "https://partners.netavo.com/api/v1/platform/webhooks/wh_0192a4c3e5f67b8d9e0f1a2b3c4d5e6f/deliveries/dlv_0192a4c3e5f67b8d9e0f1a2b3c4d5e71/redeliver" \
  -H "Authorization: Bearer $THINGSIM_API_KEY" \
  -H "Idempotency-Key: 5d1c7a0e-2f4b-4e8a-9c61-3b7f0d2e8a14"

Python

import os
import requests

response = requests.post(
    "https://partners.netavo.com/api/v1/platform/webhooks/wh_0192a4c3e5f67b8d9e0f1a2b3c4d5e6f/deliveries/dlv_0192a4c3e5f67b8d9e0f1a2b3c4d5e71/redeliver",
    headers={
        "Authorization": f"Bearer {os.environ['THINGSIM_API_KEY']}",
        "Idempotency-Key": "5d1c7a0e-2f4b-4e8a-9c61-3b7f0d2e8a14",
    },
)
response.raise_for_status()
print(response.json())

Node

const response = await fetch("https://partners.netavo.com/api/v1/platform/webhooks/wh_0192a4c3e5f67b8d9e0f1a2b3c4d5e6f/deliveries/dlv_0192a4c3e5f67b8d9e0f1a2b3c4d5e71/redeliver", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.THINGSIM_API_KEY}`,
    "Idempotency-Key": "5d1c7a0e-2f4b-4e8a-9c61-3b7f0d2e8a14",
  },
});
if (!response.ok) throw new Error(await response.text());
console.log(await response.json());
Response · 202
{
  "id": "dlv_0192a4c3e5f67b8d9e0f1a2b3c4d5e71",
  "eventId": "evt_0192a4c3e5f67b8d9e0f1a2b3c4d5e72",
  "eventType": "iot.sim.allowance_reached",
  "attempt": 1,
  "outcome": "succeeded",
  "responseStatus": 200,
  "durationMs": 184,
  "error": null,
  "attemptedAt": "2026-09-25T09:12:45Z",
  "nextAttemptAt": null
}
Example error · 404
404 · application/problem+json
{
  "type": "https://partners.netavo.com/api/problems/not-found",
  "title": "Not found",
  "status": 404,
  "detail": "No such resource in your account.",
  "instance": "/api/v1/platform/webhooks/{webhookId}/deliveries/{deliveryId}/redeliver",
  "code": "not-found",
  "traceId": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"
}

POST/v1/platform/webhooks/{webhookId}/rotate-secret

Rotate a webhook's signing secret

Issues a new secret. For 24 hours events carry signatures from both secrets, so you can deploy the new one without dropping events.

Scope
platform.webhooks:manage
Idempotent
Send an Idempotency-Key header

Parameters

  • webhookId string path required
    The webhook's id.

    Example wh_0192a4c3e5f67b8d9e0f1a2b3c4d5e6f

Returns 200 SecretRotation

The new secret.

Errors

  • 401unauthorizedThe bearer token is missing, expired or revoked.
  • 403insufficient-scopeThe key is valid but lacks the platform.webhooks:manage scope.
  • 404not-foundNot found, or not in your account. Resources outside your organisation are always 404, never 403.
  • 422idempotency-key-reusedThe Idempotency-Key was already used for a different request.
  • 429rate-limitedRate limit exceeded. Wait for Retry-After seconds.

curl

curl -X POST "https://partners.netavo.com/api/v1/platform/webhooks/wh_0192a4c3e5f67b8d9e0f1a2b3c4d5e6f/rotate-secret" \
  -H "Authorization: Bearer $THINGSIM_API_KEY" \
  -H "Idempotency-Key: 5d1c7a0e-2f4b-4e8a-9c61-3b7f0d2e8a14"

Python

import os
import requests

response = requests.post(
    "https://partners.netavo.com/api/v1/platform/webhooks/wh_0192a4c3e5f67b8d9e0f1a2b3c4d5e6f/rotate-secret",
    headers={
        "Authorization": f"Bearer {os.environ['THINGSIM_API_KEY']}",
        "Idempotency-Key": "5d1c7a0e-2f4b-4e8a-9c61-3b7f0d2e8a14",
    },
)
response.raise_for_status()
print(response.json())

Node

const response = await fetch("https://partners.netavo.com/api/v1/platform/webhooks/wh_0192a4c3e5f67b8d9e0f1a2b3c4d5e6f/rotate-secret", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.THINGSIM_API_KEY}`,
    "Idempotency-Key": "5d1c7a0e-2f4b-4e8a-9c61-3b7f0d2e8a14",
  },
});
if (!response.ok) throw new Error(await response.text());
console.log(await response.json());
Response · 200
{
  "signingSecret": "whsec_9pD2kR5tV8xA1cF4hJ7mN0qS3uW6yZ9b",
  "previousSecretExpiresAt": "2026-09-26T09:30:00Z"
}
Example error · 404
404 · application/problem+json
{
  "type": "https://partners.netavo.com/api/problems/not-found",
  "title": "Not found",
  "status": 404,
  "detail": "No such resource in your account.",
  "instance": "/api/v1/platform/webhooks/{webhookId}/rotate-secret",
  "code": "not-found",
  "traceId": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"
}

POST/v1/platform/webhooks/{webhookId}/test

Send a test event

Queues a platform.webhook.test event to this endpoint only. Check the delivery log for the result.

Scope
platform.webhooks:manage
Idempotent
Send an Idempotency-Key header

Parameters

  • webhookId string path required
    The webhook's id.

    Example wh_0192a4c3e5f67b8d9e0f1a2b3c4d5e6f

Returns 202 TestEventResult

Queued.

Errors

  • 401unauthorizedThe bearer token is missing, expired or revoked.
  • 403insufficient-scopeThe key is valid but lacks the platform.webhooks:manage scope.
  • 404not-foundNot found, or not in your account. Resources outside your organisation are always 404, never 403.
  • 422idempotency-key-reusedThe Idempotency-Key was already used for a different request.
  • 429rate-limitedRate limit exceeded. Wait for Retry-After seconds.
  • 503service-unavailableThe event could not be queued. Retry shortly.

curl

curl -X POST "https://partners.netavo.com/api/v1/platform/webhooks/wh_0192a4c3e5f67b8d9e0f1a2b3c4d5e6f/test" \
  -H "Authorization: Bearer $THINGSIM_API_KEY" \
  -H "Idempotency-Key: 5d1c7a0e-2f4b-4e8a-9c61-3b7f0d2e8a14"

Python

import os
import requests

response = requests.post(
    "https://partners.netavo.com/api/v1/platform/webhooks/wh_0192a4c3e5f67b8d9e0f1a2b3c4d5e6f/test",
    headers={
        "Authorization": f"Bearer {os.environ['THINGSIM_API_KEY']}",
        "Idempotency-Key": "5d1c7a0e-2f4b-4e8a-9c61-3b7f0d2e8a14",
    },
)
response.raise_for_status()
print(response.json())

Node

const response = await fetch("https://partners.netavo.com/api/v1/platform/webhooks/wh_0192a4c3e5f67b8d9e0f1a2b3c4d5e6f/test", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.THINGSIM_API_KEY}`,
    "Idempotency-Key": "5d1c7a0e-2f4b-4e8a-9c61-3b7f0d2e8a14",
  },
});
if (!response.ok) throw new Error(await response.text());
console.log(await response.json());
Response · 202
{
  "eventId": "evt_0192a4c3e5f67b8d9e0f1a2b3c4d5e70"
}
Example error · 404
404 · application/problem+json
{
  "type": "https://partners.netavo.com/api/problems/not-found",
  "title": "Not found",
  "status": 404,
  "detail": "No such resource in your account.",
  "instance": "/api/v1/platform/webhooks/{webhookId}/test",
  "code": "not-found",
  "traceId": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"
}

Platform schemas

The objects the Platform API sends and receives.

ApiKey object

An API key. The secret is never returned after creation.

  • id string
    The key's id, key_….

    Example key_0192a4c3e5f67b8d9e0f1a2b3c4d5e6f

  • name string
    What the key is for.

    Example Fleet dashboard

  • scopes string[]
    The scopes the key may use.

    Example ["iot.sims:read","iot.usage:read"]

  • status string
    active, expired or revoked.

    Example active

  • allowedIpRanges string[]
    Source addresses and CIDR ranges the key is limited to; empty for anywhere.

    Example ["203.0.113.0/24"]

  • createdAt string (date-time)

    Example 2026-09-25T10:00:00Z

  • expiresAt string (date-time)

    Example 2027-09-25T10:00:00Z

  • lastUsedAt string (date-time) or null
    When the key was last used, to the nearest few minutes; null if never.

    Example 2026-09-25T11:30:00Z

  • revokedAt string (date-time) or null
    When the key was revoked; null unless it has been.

ApiKeyCreate object

A new API key.

  • name string required
    What the key is for, so you can recognise it later.

    Example Fleet dashboard

  • scopes string[] required
    The scopes the key may use. Each must be one the calling credential holds itself: a key can never do more than the key that created it.

    Example ["iot.sims:read","iot.usage:read"]

  • expiresAt string (date-time) or null
    When the key stops working, at most a year ahead (and no later than the calling key's own expiry). Leave out for a year.

    Example 2027-03-31T00:00:00Z

  • allowedIpRanges string[] or null
    Limit the key to these source addresses or CIDR ranges. Leave out for anywhere — unless the calling key is itself limited, in which case the new key inherits its limits and may only narrow them.

    Example ["203.0.113.0/24"]

ApiKeyCreated object

A created key and its secret.

  • apiKey ApiKey
    An API key. The secret is never returned after creation.
  • secret string
    The key itself — send it as Authorization: Bearer …. Shown only in this response: store it now; it cannot be read back.

    Example eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.…

BrandingResponse object

API response model for organisation branding information Includes inline SVG logo content for authenticated users and extensions

  • organisationId string (uuid)
    Organisation ID that owns this branding
  • organisationName string or null
    Organisation name to display in the application
  • primaryColor string or null
    Primary brand color (hex format, e.g., #5A66F1)
  • secondaryColor string or null
    Secondary brand color (hex format, e.g., #60A5FA)
  • supportPhone string or null
    Support contact phone number
  • supportEmail string or null
    Support contact email address
  • logoSvg string or null
    SVG logo content inline (full SVG markup)
  • isInherited boolean
    Indicates if this branding is inherited from a parent organisation

BrandingResponseApiResponse object

Standard API response envelope for success responses

  • success boolean
  • API response model for organisation branding information Includes inline SVG logo content for authenticated users and extensions
  • message string or null

ErrorDetail object

  • code string
  • message string
  • details map of string[] or null

EventList object

A page of results. Follow nextCursor for the next one.

  • data WebhookEvent[] required
    This page of results.
  • hasMore boolean required
    Whether another page follows.

    Example false

  • nextCursor string or null
    Pass as startingAfter to get the next page. Null on the last page.

NotificationType object

One entry in the notification catalogue.

  • type string required

    Example iot.sim.allowance_reached

  • serviceArea ServiceArea required
    The product area a notification belongs to; platform is the account itself.

    One of voice connectivity hosting iot platform

  • description string required

    Example A SIM used its whole data allowance for the period and has stopped passing data.

  • optIn boolean required
    High-volume types are only sent to webhooks that name them in types.

    Example false

  • available boolean required
    False for a type that is catalogued (so it can be subscribed to and documented) but not yet raised by anything. Subscribing to it is allowed; nothing arrives until it is.

    Example true

  • channels NotificationChannel[] required

    One of webhook email portal

  • examplePayload object required
    An example of the type's data object.

Problem object

An RFC 9457 problem document. type and code are stable; title and detail are for people.

  • type string
    Identifies the kind of problem.

    Example https://partners.netavo.com/api/problems/not-found

  • title string
    Short, human-readable summary of the problem type.

    Example Not found

  • status integer
    The HTTP status code.

    Example 404

  • detail string or null
    What went wrong with this request.

    Example No SIM with ICCID 8944110068212345678 in your account.

  • instance string or null
    The request path.

    Example /api/v1/iot/sims/8944110068212345678

  • code string
    Stable machine-readable code, the last segment of type.

    Example not-found

  • traceId string or null
    Quote this if you contact support.

    Example 00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01

  • errors map of string[] or null
    Per-field messages when code is validation-failed.

SecretRotation object

A new signing secret. Events are signed with both secrets until previousSecretExpiresAt.

  • signingSecret string required

    Example whsec_9pD2kR5tV8xA1cF4hJ7mN0qS3uW6yZ9b

  • previousSecretExpiresAt string (date-time) required
    Events are signed with both secrets until then.

    Example 2026-09-26T09:30:00Z

ServiceArea enum

The product area a notification belongs to; platform is the account itself.

voice connectivity hosting iot platform

TestEventResult object

The test event that was queued.

  • eventId string required
    The event's id; find its delivery in the webhook's delivery log.

    Example evt_0192a4c3e5f67b8d9e0f1a2b3c4d5e70

Webhook object

A webhook endpoint.

  • id string required
    The webhook's id, wh_….

    Example wh_0192a4c3e5f67b8d9e0f1a2b3c4d5e6f

  • organisationId string (uuid) required
    The organisation that owns the webhook.

    Example 0f8b6c1e-5a2d-4c7e-9b3a-2d1e6f4a7c90

  • url string required
    Where we POST events. HTTPS on a public address; redirects are not followed.

    Example https://example.com/hooks/thingsim

  • description string or null

    Example Ops alerts

  • types string[] required
    Notification types to send. Empty with empty serviceAreas means every type your account can receive, except opt-in types.

    Example ["iot.sim.allowance_reached","iot.sim.usage_threshold"]

  • serviceAreas ServiceArea[] required
    Send every type in these service areas.

    One of voice connectivity hosting iot platform

  • includeSubtree boolean required
    Partners: also send events for customer organisations below you.

    Example false

  • enabled boolean required

    Example true

  • status string required
    active, failing while deliveries are being retried, or disabled (switched off, or after a day of failures).

    Example active

  • disabledReason string or null
    Why a disabled webhook was switched off.
  • createdAt string (date-time) required

    Example 2026-09-20T10:00:00Z

  • updatedAt string (date-time) required

    Example 2026-09-20T10:00:00Z

WebhookCreate object

A new webhook endpoint.

  • url string required
    Where we POST events. HTTPS on a public address; redirects are not followed.

    Example https://example.com/hooks/thingsim

  • description string or null

    Example Ops alerts

  • types string[] or null
    Notification types to send. Leave out (with serviceAreas) for every type your account can receive, except opt-in types.

    Example ["iot.sim.allowance_reached","iot.sim.usage_threshold"]

  • serviceAreas ServiceArea[] or null
    Send every type in these service areas.

    One of voice connectivity hosting iot platform

  • includeSubtree boolean or null
    Partners: also send events for customer organisations below you. Null or left out is false.

    Example false

  • organisationId string (uuid) or null
    Partners: create the webhook for a customer organisation below you. Defaults to your own organisation.

WebhookCreated object

The new webhook and its signing secret. The secret is only ever returned here and by rotate-secret: store it now.

  • webhook Webhook required
    A webhook endpoint.
  • signingSecret string required
    Verifies the Webhook-Signature header.

    Example whsec_3yJ8mQ2vN6pR9tW1zC4fH7kL0sX5bE8g

WebhookDelivery object

One attempt to deliver an event to a webhook.

  • id string required
    This attempt, dlv_…. Pass it to redeliver the event.

    Example dlv_0192a4c3e5f67b8d9e0f1a2b3c4d5e71

  • eventId string required

    Example evt_0192a4c3e5f67b8d9e0f1a2b3c4d5e72

  • eventType string required

    Example iot.sim.allowance_reached

  • attempt integer required

    Example 1

  • outcome string required
    succeeded, failed or pending (queued, not yet attempted).

    Example succeeded

  • responseStatus integer or null
    Your endpoint's HTTP status; null if it could not be reached.

    Example 200

  • durationMs integer required

    Example 184

  • error string or null
  • attemptedAt string (date-time) required

    Example 2026-09-25T09:12:45Z

  • nextAttemptAt string (date-time) or null
    When a failed delivery will be retried.

WebhookDeliveryList object

A page of results. Follow nextCursor for the next one.

  • data WebhookDelivery[] required
    This page of results.
  • hasMore boolean required
    Whether another page follows.

    Example false

  • nextCursor string or null
    Pass as startingAfter to get the next page. Null on the last page.

WebhookEvent object

The body of every webhook POST, and an item in the event log. data depends on type: see the notification catalogue.

  • id string required
    evt_…. Unique per event; deliveries can repeat, so de-duplicate on it.

    Example evt_0192a4c3e5f67b8d9e0f1a2b3c4d5e72

  • type string required
    The notification type.

    Example iot.sim.allowance_reached

  • serviceArea ServiceArea required
    The product area a notification belongs to; platform is the account itself.

    One of voice connectivity hosting iot platform

  • organisationId string (uuid) required
    The organisation the event is about.

    Example 0f8b6c1e-5a2d-4c7e-9b3a-2d1e6f4a7c90

  • occurredAt string (date-time) required
    When the change took effect.

    Example 2026-09-25T09:12:44Z

  • data object required
    The type's payload; see the notification catalogue.

    Example {"iccid":"8944110068212345678","label":"Car park barrier 3","planId":"5b0e4f2a-8c1d-4e6f-9a3b-7d2c1e0f4a61","allowanceBytes":1073741824,"usedBytes":1073741824,"periodEnd":"2026-10-01T00:00:00Z"}

WebhookList object

A page of results. Follow nextCursor for the next one.

  • data Webhook[] required
    This page of results.
  • hasMore boolean required
    Whether another page follows.

    Example false

  • nextCursor string or null
    Pass as startingAfter to get the next page. Null on the last page.

WebhookUpdate object

Fields to change. Leave a field out to keep it.

  • url string or null
    Where we POST events. HTTPS on a public address; redirects are not followed.

    Example https://example.com/hooks/thingsim

  • description string or null

    Example Ops alerts

  • types string[] or null
    Replaces the types sent.

    Example ["iot.sim.allowance_reached"]

  • serviceAreas ServiceArea[] or null
    Replaces the service areas sent.

    One of voice connectivity hosting iot platform

  • includeSubtree boolean or null

    Example false

  • enabled boolean or null
    Re-enabling a disabled webhook resets its failure count.

    Example true