Skip to main content

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 toRead
Send orders from your systemCreating orders
Understand statuses and reason codesStatuses and reasons
Get statuses back into your systemStatus updates: polling and webhooks
Text the recipientRecipient SMS
Fetch photos and signaturesProof of delivery
Plan a test runERP 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.

Use a separate test project

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.

APIBase URLUsed for
Ordershttps://orders-api.cloud.zoopit.noOrders, order actions (proof of delivery), webhooks
Routeshttps://routes-api.cloud.zoopit.noRoutes, activities, planning
Notificationshttps://notifications-api.cloud.zoopit.noSMS 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​

WhatLimit
limit on GET /v1/orders and GET /v1/order_actions1000 per page. Above that the request fails with 406.
Default page size when you send no limit10 on GET /v1/orders. Always send limit.
limit on the Notifications API lists50 per page (406 above it)
limit on webhook subscription and delivery lists1000 per page (higher values are capped)
Request body4 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'
Bracket notation is silently ignored

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.

Order lists are limited to the last 365 days

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​

StatusMeans
400The request was understood but is not valid, for example an unknown reason code. The message says what is wrong.
401Missing, invalid or expired token.
403The token is valid but lacks the permission or the project.
404Not found — or, on a PATCH, the modified_at you sent is no longer current (see updating).
406A query parameter is out of range, for example limit above the maximum.
5xxOur 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.