Documentation
Getting a key
- An administrator of your organisation requests a key above, giving it a name and its intended use.
- Atlas reviews every request, then approves or refuses it with a reason. The decision shows here.
- Once the key is approved, the person who requested it reveals it ONCE and stores it in a secrets vault.
- The key is bound to your organisation AND to the account that requested it: if that account leaves the organisation or is blocked, the key stops working.
Authentication
Every request carries the key in the Authorization header: “Authorization: Bearer <your key>”. Never in the address (URL): addresses end up in logs, histories and proxies.
The API is meant for SERVER-TO-SERVER calls. A request carrying an Origin header (i.e. sent from a browser) is refused (403 browser_origin_refused): a key must never sit in code that runs on your visitors’ devices.
A leaked key can be revoked immediately above; “Rotate” issues a new one and cuts the old one off on the spot.
Authorization: Bearer <key> Base URL
https://api.astro.atlascontrole.be/api/client/v1 Every route is relative to this address, HTTPS only. Responses are JSON (UTF-8), dates in ISO 8601.
Routes
| Route | What it returns | Parameters |
|---|---|---|
GET /me | The key in use, your organisation, your limits and today’s usage. | — |
GET /inspections | Your inspections, sorted by ascending update date (ideal for syncing). | status, from, to, updated_since, limit, cursor |
GET /inspections/{ref} | One inspection, by its reference. | — |
GET /orders | Your orders and quotes. | status, limit, cursor |
GET /invoices | Your invoices, 20 per page. Only for keys held by an administrator of the organisation. | status, year, cursor |
GET /sites | Your properties (addresses) and the installations on them, 20 per page. | cursor |
GET /catalog | What you can order, as the client portal shows it to you: services, options, your prices. Use it to compose the lines of POST /orders. | — |
GET /openapi.json | The OpenAPI description of the API. No key needed. | — |
Key permissions
A key can always read. It writes only for the permissions requested with the key (or later, with “Change permissions”) AND granted by Atlas, one by one. A key created before October 2026 stays read-only until a new approval. GET /me returns the key’s permissions (scopes) and those awaiting Atlas (pendingScopes).
read- Read: inspections, orders, invoices, properties. Always included.
documents:write- Upload documents (diagrams and plans) to an order or an inspection.
messages:write- Post messages on an order or inspection thread.
orders:write- Price, order, request a quote, withdraw from an order, ask to be called back.
quotes:sign- Sign and confirm a quote. A separate permission, granted on its own by Atlas: it is not part of orders:write.
appointments:write- Ask to move or cancel a planned visit.
complaints:write- File a complaint and reply in its case.
payments- Get an invoice’s online payment link — the only payment action of the API: no card payment, no payment of an order.
sites:write- Add or archive a property.
organization:write- Invite, change the role of or remove a member; decide on a join request; billing address.
directory:write- Manage your client and contractor records.
sharing:write- Open or close a contractor’s access to your orders.
Writing: the rules
Each write is an action of the client portal, judged BY the client portal with the rights of the person who requested the key: same checks, same prices, same slot rules, same emails. What the client portal refuses that person, the API refuses too, with the same reason (error.code).
Files are sent as JSON: { "name", "mimeType", "dataBase64" }, at most 5 per request, PDF up to 15 MB, image up to 5 MB, 30 MB in total. The type is checked against the file’s content: a file that is not what it claims is refused (attachment_type).
A success returns { "data": { … } } (201 for a creation); data carries what the client portal answers for that action.
Writes have their own daily quota: 600 writes per key per day (returned by GET /me), on top of the general limits.
Not available through the API for now: sharing a report link, uploading remediation photos, batch-signing quotes and exporting your personal data (GDPR). Do these in the client portal.
Withdrawing from an order follows the client portal’s rule exactly: the API allows it when the client portal does.
| Route | What it does | Permission |
|---|---|---|
POST /orders/{id}/documents | Upload diagrams and plans to an order (annexDocType: unifilaire, position or other; taskRefs to target visits). | documents:write |
POST /inspections/{ref}/documents | Upload diagrams and plans to an inspection. | documents:write |
POST /orders/{id}/messages | Post on an order thread (photos and PDF allowed). | messages:write |
POST /inspections/{ref}/messages | Post on an inspection thread. | messages:write |
POST /orders/{id}/complaints | File a complaint about an order (PL-… case, acknowledged by email). | complaints:write |
POST /inspections/{ref}/complaints | File a complaint about an inspection. | complaints:write |
POST /complaints/{reference}/messages | Reply in a complaint file. | complaints:write |
POST /orders/pricing | Price a basket, like the client portal (writes nothing). | orders:write |
POST /orders/slots | Propose visit slots (writes nothing): a slotToken to pass to POST /orders. | orders:write |
POST /orders | Order: same prices, slots and confirmation email as the client portal. | orders:write |
POST /quotes | Request a quote (Atlas prices it; nothing is ordered). | orders:write |
POST /quotes/{reference}/cancel | Cancel a quote request. | orders:write |
POST /orders/{id}/sign | Sign and confirm a quote. | quotes:sign |
POST /orders/{id}/withdrawal | Withdraw from an order, when the client portal allows it. | orders:write |
POST /requests | Ask to be contacted about a service. | orders:write |
POST /inspections/{ref}/visit-change | Ask to move (reschedule) or cancel a visit. | appointments:write |
POST /invoices/{id}/payment-link | The online payment link of an invoice (administrators). | payments |
POST /sites | Add a property (address checked with bpost). | sites:write |
POST /sites/archive | Archive a property without history. | sites:write |
POST /organization/invitations | Invite a person (they receive the invitation email). | organization:write |
POST /organization/invitations/revoke | Revoke a pending invitation. | organization:write |
POST /organization/members/role | Change a member’s role. | organization:write |
POST /organization/members/remove | Remove a member. | organization:write |
POST /organization/join-requests/decide | Accept or refuse a join request. | organization:write |
POST /organization/billing | Billing email address. | organization:write |
POST /clients, /clients/update, /clients/archive | Your client records. | directory:write |
POST /contractors, /contractors/update, /contractors/archive, /contractors/merge | Your contractor records. | directory:write |
POST /contractor-links, /contractor-links/{id}/revoke, /contractor-links/{id}/grants/{grantId}/revoke | Open or close a contractor’s access to your orders. | sharing:write |
Idempotency-Key: never the same action twice
Every write carries a unique Idempotency-Key header (8 to 255 characters, e.g. a UUID). Without it: 400 idempotency_key_required.
If the answer is lost (network cut, timeout), send the SAME request with the SAME key: the API replays the first answer (header Idempotent-Replayed: true) instead of doing the action again — an order is never placed twice. The key is kept 24 hours.
The same key for a different request is refused (422 idempotency_key_reused). After a 429 or a 5xx error nothing is kept: retry with the same key.
POST /orders/pricing and POST /orders/slots write nothing and need none.
An inspection, field by field
ref, state, completion (status: done / cancelled / not-done; doneIn), scheduledStart, scheduledEnd, type and subtype (fr / nl), title, address (fr / nl), inspectorName, order (id, reference, clientReference, paymentStatus), report (status, conclusion, issuedConclusion, held, pdf fr / nl, portalUrl), nextDueDate, updatedAt. report.status is platform, held, awaiting-payment, pending, unavailable, unverified or null; report.pdf links are signed and open without a key.
A property: key, address and installations (id, reference, type fr / nl, lastInspectionRef, lastInspectionAt, nextDueDate, conclusion).
Examples
Check the key and read your limits:
curl -sS --compressed \
-H "Authorization: Bearer $ATLAS_API_KEY" \
https://api.astro.atlascontrole.be/api/client/v1/me Read the inspections completed since 1 October, 100 per page:
curl -sS --compressed \
-H "Authorization: Bearer $ATLAS_API_KEY" \
"https://api.astro.atlascontrole.be/api/client/v1/inspections?status=done&from=2026-10-01&limit=100" Next page: send the same request again with the cursor returned by the previous one.
curl -sS --compressed \
-H "Authorization: Bearer $ATLAS_API_KEY" \
"https://api.astro.atlascontrole.be/api/client/v1/inspections?status=done&from=2026-10-01&limit=100&cursor=<nextCursor>" Order (a slot proposed by POST /orders/slots):
curl -sS --compressed -X POST \
-H "Authorization: Bearer $ATLAS_API_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{"lines":[{"service":"<service>","qty":1}],"address":{"street":"Rue de la Loi 16","zip":"1000","city":"Bruxelles"},"phone":"+32 2 000 00 00","slotToken":"<slotToken>"}' \
https://api.astro.atlascontrole.be/api/client/v1/orders Upload a single-line diagram to an inspection:
{
"annexDocType": "unifilaire",
"attachments": [
{ "name": "unifilaire.pdf", "mimeType": "application/pdf", "dataBase64": "JVBERi0xLjcK…" }
]
} curl -sS --compressed -X POST \
-H "Authorization: Bearer $ATLAS_API_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d @document.json \
https://api.astro.atlascontrole.be/api/client/v1/inspections/DOM-2026-ABC123/documents Cursor pagination
Lists return { "data": [...], "nextCursor": "…" }. As long as nextCursor is not null, pass it unchanged in the cursor parameter to get the next page.
The cursor is opaque: do not decode it, do not build it. limit ranges from 1 to 100 (50 by default) and applies to /inspections and /orders only: /invoices and /sites return fixed pages of 20.
For incremental syncing, keep the most recent updatedAt you received and start from updated_since=<that date> on the next run. The filter uses the record’s update date; some background changes do not move it, so also schedule a periodic full resync (weekly, for instance).
{
"data": [ { "ref": "…", "state": "done", "updatedAt": "2026-10-02T09:14:00.000Z", … } ],
"nextCursor": "eyJ1IjoiMjAyNi0xMC0wMlQwOToxNDowMC4wMDBaIn0"
} Filters
- Inspections — status: todo, in_progress, ready, done, cancelled; from and to: scheduled date, YYYY-MM-DD; updated_since: ISO 8601 date.
- Orders — status: quotation, order, cancelled.
- Invoices — status: paid, unpaid; year: YYYY.
- An unknown or malformed parameter returns 400 invalid_parameter, naming the parameter in the message.
Caching: ETag and If-None-Match
Every response carries an ETag header. Send it back in If-None-Match on the next call: if nothing changed, the API answers 304 Not Modified, with no body.
Responses from /orders, /invoices and /sites are cached for up to 60 seconds per key: a change may take a minute to show.
Ask for compression (Accept-Encoding: gzip, br): responses are compressed in transit.
curl -sS -i --compressed \
-H "Authorization: Bearer $ATLAS_API_KEY" \
-H 'If-None-Match: "<ETag>"' \
https://api.astro.atlascontrole.be/api/client/v1/sites Rate limits and quota
Each key has a per-minute limit and a daily quota (by default 60 requests per minute and 10,000 per day; your exact values are shown above and returned by GET /me).
Beyond that, the API answers 429 (rate_limited per minute, quota_exceeded per day) with a Retry-After header in seconds: wait that long before retrying.
Writes: a separate daily quota (600 per key by default), on top of these limits; beyond it, 429 write_quota_exceeded.
X-RateLimit-Limit- Requests allowed per minute.
X-RateLimit-Remaining- Requests left in the current minute.
X-RateLimit-Reset- Seconds until the one-minute window ends.
X-RateLimit-Limit-Day- Requests allowed per day.
X-RateLimit-Remaining-Day- Requests left today.
X-RateLimit-Remaining-Write-Day- Writes left today.
Errors
An error always has the same shape: { "error": { "code": "…", "message": "…" } }. The code is stable; the message is meant for humans and may change.
{ "error": { "code": "rate_limited", "message": "…" } } | Status | Code | Meaning |
|---|---|---|
| 400 | invalid_parameter | Unknown or malformed parameter. |
| 401 | unauthorized | Missing or unknown key. |
| 401 | key_revoked | Key revoked. |
| 401 | key_expired | Key expired. |
| 401 | key_not_active | Key not active yet (pending, or not revealed yet). |
| 403 | forbidden | Access to this resource is denied. |
| 403 | browser_origin_refused | Call sent from a browser (Origin header). |
| 403 | invoices_forbidden | The key holder is no longer an administrator of the organisation. |
| 404 | not_found | Resource does not exist, or is outside your organisation. |
| 429 | rate_limited | Per-minute limit reached (see Retry-After). |
| 429 | quota_exceeded | Daily quota reached (see Retry-After). |
| 400 | invalid_body | Missing or malformed request body. |
| 400 | idempotency_key_required | Write without an Idempotency-Key header. |
| 400 | invalid_idempotency_key | Malformed Idempotency-Key (8 to 255 characters). |
| 403 | insufficient_scope | The key has not been granted the required permission (see error.details.required). |
| 409 | idempotency_in_progress | A request with this Idempotency-Key is still running: retry in a few seconds. |
| 413 | payload_too_large | Request too large. |
| 422 | idempotency_key_reused | This Idempotency-Key was already used for a different request. |
| 429 | write_quota_exceeded | Daily write quota reached (see Retry-After). |
| 500 | internal_error | Unexpected error on our side: try again later. |
| 502 | upstream_unavailable | Data source briefly unreachable: try again later. |
| 503 | disabled | API temporarily disabled. |
OpenAPI description
The full description (schemas, parameters, responses) is published as OpenAPI 3, readable by most tools (Postman, Insomnia, client generators):
Changelog
- v1 — 2026-10
- First version, read-only.
- v1.1 — 2026-10
- Writes: client-portal actions per granted permission, Idempotency-Key, write quota.