Getting started
Five minutes from nothing to a working integration. Everything here runs against stage — swap the host for production once it works.
Stage https://api.stage.theattco.net
Production https://api.theattco.net1. Get a token
Every request carries a bearer token. As a person, log in:
TOKEN=$(curl -s https://api.stage.theattco.net/v2/auth/login \
-H 'content-type: application/json' \
-d '{"email":"you@example.com","password":"…"}' | jq -r .access_token)As a machine, exchange an API key for one instead — see Authentication for how the two halves of a key work:
TOKEN=$(curl -s https://api.stage.theattco.net/v2/auth/api-token \
-H 'X-Access-Key-Id: tac_live_7Qk2RfNp4xZ1mVdB' \
-H 'X-Access-Key-Secret: …' | jq -r .access_token)2. Make a call
curl -s https://api.stage.theattco.net/v2/locations \
-H "Authorization: Bearer $TOKEN"{
"entries": [
{
"id": "loc_018f5a01-1c2d-7e3f-8a4b-5c6d7e8f9012",
"name": "Kungsgatan",
"timezone": "Europe/Stockholm"
}
],
"next_cursor": null
}That is the whole shape of the API: a JSON object, snake_case throughout, with list endpoints returning entries and a next_cursor.
3. The token already knows who you are
Almost no endpoint takes an account id. The token identifies both the person and the account they are working in, and everything you can reach is scoped to that account automatically.
If you find yourself looking for somewhere to pass an account id, you are probably calling an internal endpoint that was not meant for you.
4. Walk a list
next_cursor is how you get the rest. Feed it back as cursor, and stop when it comes back null — not when a page looks short:
cursor=""
while :; do
page=$(curl -s "https://api.stage.theattco.net/v2/locations?limit=100${cursor:+&cursor=$cursor}" \
-H "Authorization: Bearer $TOKEN")
echo "$page" | jq -c '.entries[]'
cursor=$(echo "$page" | jq -r '.next_cursor // empty')
[ -z "$cursor" ] && break
donePagination covers why it is a cursor and not a page number.
5. Handle a failure
Every failure, from every part of the API, comes back in the same shape:
{
"type": "https://api.theattco.com/problems/validation-error",
"title": "Validation failed",
"status": 400,
"detail": "timezone must be an IANA zone name, got 'CET+1'"
}Switch on type, show detail, log all four. Errors lists every type the API can return and which ones are worth retrying.
Where to go next
| If you want to | Read |
|---|---|
| Understand tokens, API keys and roles | Authentication |
| Handle failures properly | Errors |
| Page through large results | Pagination |
| Know what ids, dates and names look like | Conventions |
| Find a specific endpoint | The API reference |
A note on ids
Ids carry a type prefix — loc_, cmp_, usr_. It makes an id self-describing in a log or a support ticket, and makes passing the wrong one obvious immediately. Treat them as opaque strings; never build one.