a key, a curl, your first to-do
Quickstart
Five minutes, no OAuth, no registration.
1. Make a personal access token
In init.Tasks: Settings → account → encryption & devices → api keys → new key. Pick the scopes you need and an expiry.
You will see the key exactly once. It looks like this:
itk_7mQ2xLp9Va4Nk1Zr8Ts6Ye3Wu0Bd5Mg2Hj7Cq4Ln1Xv8It carries your encryption key. Treat it like a password manager entry, not like an API key you paste into a chat.
2. Check it works
curl -s https://api.inittasks.com/v1/me \
-H "Authorization: Bearer $INITTASKS_TOKEN"{
"sub": "8Kd2mQ...",
"scopes": ["todos:read", "todos:write"],
"grant": { "id": "gr_4f2a", "kind": "pat", "clientId": null },
"webhooks": { "disabled": 0 }
}3. Create a to-do
curl -s https://api.inittasks.com/v1/todos \
-X POST \
-H "Authorization: Bearer $INITTASKS_TOKEN" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"title":"buy milk","whenDate":"2026-09-16","tags":["errands"]}'The title is encrypted on the way in and decrypted on the way out. errands is created if it does not exist — you do not have to pre-register a vocabulary.
4. Read it back
curl -s "https://api.inittasks.com/v1/todos?limit=5" \
-H "Authorization: Bearer $INITTASKS_TOKEN"{
"data": [{ "id": "8E4F2A1B", "title": "buy milk", "whenDate": "2026-09-16", "tags": ["errands"] }],
"next_cursor": null
}Retrying safely
This is the part most integrations get wrong, so it is worth doing properly from the start.
A write that times out may still have succeeded. Retrying it without an Idempotency-Key creates a second to-do. With one, the server replays the original response instead.
#!/usr/bin/env bash
# One key per logical operation — generated ONCE, reused by every retry.
KEY=$(uuidgen)
for attempt in 1 2 3 4 5; do
response=$(curl -s -w '\n%{http_code}' https://api.inittasks.com/v1/todos \
-X POST \
-H "Authorization: Bearer $INITTASKS_TOKEN" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $KEY" \
-d '{"title":"buy milk"}')
status=$(printf '%s' "$response" | tail -n1)
body=$(printf '%s' "$response" | sed '$d')
case "$status" in
2*) printf '%s\n' "$body"; exit 0 ;;
429|5*)
# Back off. `Retry-After` is on 429s; this doubles from one second.
sleep $(( 2 ** (attempt - 1) ))
;;
*) printf 'giving up: %s\n' "$body" >&2; exit 1 ;;
esac
doneThree rules behind it:
- Generate the key once, outside the loop. A key generated per attempt defeats the whole mechanism — that is the actual bug this example exists to prevent.
- Retry only on 429 and 5xx. A
validation_failedwill fail identically forever. - **Reuse the key with the same body.** A different body under the same key is a conflict, on purpose: it means two different operations were given the same name.
Next
- Scopes and OAuth: authentication
- What the server can and cannot see: encryption
- Every route: reference