Octopus
Octopus Developersgateway.octopusoperations.co.za/v1

Start here

Tips & gotchas

Every item on this page cost somebody real time. None of them are hypothetical, and none are things you could reasonably have guessed.

Traps

These fail in ways that look like something else: a correct-looking implementation that silently does not work, or an error that points at the wrong culprit.

Verify webhooks against the raw body

Check the signature before your JSON middleware touches the request. Parsing and re-serialising changes key order and whitespace, so the HMAC will never match. This is the single most common reason a correct-looking implementation still fails.

The key is shown exactly once

Octopus stores only a hash, so it genuinely cannot be shown again, by anyone, including support. Copy it into your secret store before you close the dialog. If you lose it, revoke it and make another.

Calendar reads are capped at 400 days

Ask for a wider window and you get a 400 explaining the limit. Page through in chunks rather than requesting a decade in one call.

A write returns before the read model catches up

A write returns as soon as the event is recorded; the row you would read is written a moment later. It is normally a few milliseconds, not seconds, but do not assert on a read taken in the same tick as the write.

Deduplicate on the delivery id

A webhook that does not answer 2xx quickly is retried, so the same event can arrive twice. x-octopus-delivery is stable across retries, key your idempotency on it.

Worth knowing

Not traps, just things that save you writing code you did not need.

Asking for .manage also grants .read

Tick tasks.manage and the key is created holding both tasks.manage and tasks.read. You never need to ask for both, and asking for both is not an error, just noise.

Never send an organisation id

It comes from the key, and anything you send is ignored. There is nothing to configure, and no way to point a key at a tenant it does not belong to.

Do not poll, subscribe

Every change you are allowed to see is pushed to the realtime channel and to any webhook you register, by the same mechanism that updates the database. Polling costs you rate limit and still arrives later.

Required fields are named, not guessed

A task needs projectId, assigneeId and dueDate. Omit one and the 400 names it exactly. Octopus will not invent a plausible value on your behalf.

The realtime namespace refuses people

/v1 is for applications. A signed-in person is turned away deliberately: the guarantees there are written in terms of scopes, and a person holds roles instead.