Getting started
This guide is for partners connecting an ERP or order system to Zoopit: your system sends orders in, Zoopit plans and drives them, and statuses, reasons and proof of delivery come back to your system. Every page marks what is live today and what is coming.
| You want to | Read |
|---|---|
| Send orders from your system | Creating orders |
| Understand statuses and reason codes | Statuses and reasons |
| Get statuses back into your system | Status updates: polling and webhooks |
| Text the recipient | Recipient SMS |
| Fetch photos and signatures | Proof of delivery |
| Plan a test run | ERP integration checklist |
Get an API key
API keys are issued by Zoopit on request. Contact us with:
- the project (account) the orders belong to, and whether you want a separate test project
- what the key must be able to do — typically create, read and update orders, and read order actions
You receive a Bearer token and its expiry date. The token is tied to one API client and carries only the permissions that were granted to it, for the projects it was granted on. Ask us for a new token before the old one expires.
You also need your project id (a UUID). It is the owner you send when you create
anything. We send it with the key.
Test orders, routes and SMS in their own project. Data is always separated by project, so nothing from a test run shows up in your live planning or reports.
Base URLs
All APIs run under *.cloud.zoopit.no, over HTTPS.
| API | Base URL | Used for |
|---|---|---|
| Orders | https://orders-api.cloud.zoopit.no | Orders, order actions (proof of delivery), webhooks |
| Routes | https://routes-api.cloud.zoopit.no | Routes, activities, planning |
| Notifications | https://notifications-api.cloud.zoopit.no | SMS channels, templates and subscriptions |
The full endpoint reference is under API reference, and interactively at Orders API and Routes API.
Authenticate
Send the token on every request, with the Bearer prefix:
GET /v1/orders?limit=1 HTTP/1.1
Host: orders-api.cloud.zoopit.no
Authorization: Bearer <token>
curl -s 'https://orders-api.cloud.zoopit.no/v1/orders?limit=1' \
-H 'Authorization: Bearer <token>'
Request and response bodies are JSON (Content-Type: application/json). Times are ISO 8601
strings; send them with a time zone (2026-12-04T09:00:00Z or 2026-12-04T10:00:00+01:00).
Every response wraps the entity in resource, with entities it references in related — see
API response.
Limits
| What | Limit |
|---|---|
limit on GET /v1/orders and GET /v1/order_actions | 1000 per page. Above that the request fails with 406. |
Default page size when you send no limit | 10 on GET /v1/orders. Always send limit. |
limit on the Notifications API lists | 50 per page (406 above it) |
limit on webhook subscription and delivery lists | 1000 per page (higher values are capped) |
| Request body | 4 MB |
There is no published per-key request-rate limit. Poll at a sensible interval (see
polling) and page with limit and
offset rather than asking for everything at once.
Query parameters are JSON
where, order and select take URL-encoded JSON, not bracket notation:
# where=[{"reference_id":{"=":"100234"}}]
curl -s -G 'https://orders-api.cloud.zoopit.no/v1/orders' \
-H 'Authorization: Bearer <token>' \
--data-urlencode 'where=[{"reference_id":{"=":"100234"}}]' \
--data-urlencode 'limit=10'
where[0][reference_id][=]=100234 (PHP or Rails style) is not parsed. The filter is dropped
and you get an unfiltered result that looks like a successful query. Build the JSON, then
URL-encode it — in PHP, http_build_query(['where' => json_encode($where)]).
Each element of where is { "<field>": { "<operator>": <value> } }. Fields inside one element
must all match. Between elements, whereOp decides: OR (the default) or AND. Common
operators: =, !=, >, >=, <, <=, IN, NOT IN, IS, IS NOT, ILIKE.
GET /v1/orders, /v1/orders/count and /v1/orders/grouped_count only return orders created in
the last 365 days unless your where contains a created_at selector. There is no error or flag
when this happens. Add a created_at selector when you need older orders.
Errors
| Status | Means |
|---|---|
400 | The request was understood but is not valid, for example an unknown reason code. The message says what is wrong. |
401 | Missing, invalid or expired token. |
403 | The token is valid but lacks the permission or the project. |
404 | Not found — or, on a PATCH, the modified_at you sent is no longer current (see updating). |
406 | A query parameter is out of range, for example limit above the maximum. |
5xx | Our fault. Retry later with backoff; see the note on retrying creates. |
Errors come in two shapes. Authentication errors come from the gateway:
{ "error": { "code": 401, "status": "Unauthorized", "message": "Access credentials are invalid" } }
Everything else comes from the service, with a request_id to quote when you contact us:
{ "request_id": "V1StGXR8_Z5jdHi6B-myT", "message": "status_reason 'canceled.other' requires a non-empty status_description" }
Read the HTTP status code first, then message. Do not parse the message text.