Skip to main content
A successful HTTP status tells you a request was accepted, not that every part of it did what you intended. This page collects the behaviors that are easy to miss when you build against examples alone, so you can tell what actually happened.

A 201 confirms creation, not every field

When you create a post, a 201 means the post was created. It is not a guarantee that every field you sent was recognized. Fields we do not recognize are not applied, so a small naming mistake (for example sending media instead of media_ids, or a misspelled key) can produce a post that publishes without the media you intended. Two habits keep you safe:
  • Send only the fields documented for the endpoint, with their exact names. Validate your request body against the API Reference before you rely on a field.
  • When a dropped field would be costly, read the object back with GET /v1/posts/{id} and confirm the field is present before you depend on it.

Verify media before you publish

The presigned upload path has four steps: request a signed URL, PUT the bytes, verify, then attach. Media that has not been verified stays in a pending state, and a pending media reference does not publish silently: on a validated publish it returns 400 (media ... is not ready or not found), and on delivery it fails that target. Always confirm your media is ready before you attach it. See Media uploads for the full flow.

Response codes

Creating or publishing a post can return three statuses. Read them, and read the per-target detail: When a target fails, the reason is on that target under targets[].error, with the shape { "category", "caused_by", "code", "message" }. Surface targets[].error.message to your users rather than a generic retry prompt. See Error handling for the code catalog.

Delete semantics

Delete does not mean the same thing on every resource. All three return 204 No Content, but what they leave behind differs: So you detect a revoked invite from a field value, but you detect a disconnected account from its absence in the list. Two different code paths.
DELETE /v1/brands/{id} is destructive. It disconnects every account under the brand and kills its pending connect links, so GET /v1/invites?brand_id=<deleted> afterward returns 404. Keep brand deletion behind a deliberate action, never in a retry or cleanup path.
The brand delete takes the ID in the path with an empty body:

IDs are opaque strings

Treat every ID we return as an opaque string. Do not parse it, and do not assume a format. Today, posts, brands, and invites are UUIDs, while accounts are prefixed with acc_, and interaction IDs carry a type prefix such as sapi_cmt_. These formats can differ per object type and may change, so persist IDs as text, not as a typed uuid column.