Errors

Every failure returns JSON in the same shape, whatever went wrong.

json
{
  "error": {
    "code": "invalid_request",
    "message": "accountId must be a valid UUID",
    "details": [{ "path": ["accountId"], "message": "Invalid uuid" }]
  }
}

code is stable and safe to branch on. message is written for a human reading a log and may change. details is present only when there is something more to say — validation issues, or the per-account errors array from an aggregate read.

Status codes

StatusWhen
200Success, or an aggregate read where every provider succeeded.
201A resource was created — an Instagram post, a message, a connection session.
202A TikTok or YouTube publish, or a Facebook video, was accepted. It is not published yet; poll for status.
207An aggregate read where some providers succeeded and some failed.
400The request is malformed: bad JSON, a failed schema check, a non-UUID identifier.
402X is not enabled for your account: x_billing_required. See Usage.
403Only from POST /api/v1/profiles. Scoped keys cannot create profiles.
404The resource does not exist, or it exists but your key cannot reach it.
413The request body is too large.
415Content-Type is neither application/json nor multipart/form-data.
422The request is well-formed but the operation is not possible: an unsupported platform, an expired connection, invalid media, a deferred feature.
429The provider is throttling this account or app (TikTok), the account hit TikTok's daily posting limit, or YouTube's shared daily quota is spent. Retry later.
502Every attempted provider failed, or a database read failed. Provider details are sanitized away.
503X is unavailable to Adeli for a while: temporarily_unavailable. Retry later.

404 covers authorization, on purpose

Supplying a profileId or accountId that belongs to someone else returns 404, not 403. The API will not confirm that a resource exists outside your key's profile. This is the single most common surprise when integrating.

Error codes

Authentication and scope

unauthorized401
The Authorization header is missing, malformed, or names a key that has been deleted.
profile_not_found404
The profileId does not exist, or it is not the profile your key is bound to.
profile_scope_violation403
Returned by POST /api/v1/profiles. Create profiles in the dashboard.

Request validation

invalid_request400 / 413
Malformed JSON, a schema failure, a non-UUID identifier, or a body over the size limit. Check details.
invalid_profile400
Profile fields failed their own checks — for example metadata over 16 KiB.
invalid_redirect_uri400
The connect redirectUrl is not a valid URL, its origin is not allowlisted, or it is not HTTPS outside localhost.
unsupported_media_type415
Content-Type must be application/json or multipart/form-data.

Conflicts

profile_name_conflict409
Another profile already uses this name. details.existingProfileId names it.
profile_external_id_conflict409
Another profile already uses this externalId. details.existingProfileId names it.

Accounts and connections

account_not_found404
No connected account with that id inside your key's profile.
connection_session_not_found404
No connection session with that id for this profile.
connection_expired422
The stored provider token has lapsed, or the required scope was never granted. The customer has to reconnect.
connection_unavailable—
Appears inside an aggregate errors array when one account's credentials could not be used for that read.
provider_not_configured422
This Adeli deployment has no credentials for that provider.
provider_not_supported422
The provider cannot do this. WhatsApp and Google Business Profile connect sessions, and TikTok and YouTube messaging, land here.
missing_scope—
A YouTube or X connect session's error.code: the customer withheld a permission on the provider's consent screen.
x_billing_required402
X is billed per call and is not enabled for your account yet. Connecting, posting, deleting, and listing X posts all return it. See Usage.
reconnect_required409
Turning on an X capability needs a permission the connection was not granted. details.missingScopes lists them.
no_channel—
A YouTube connect session's error.code: the Google account chosen has no YouTube channel.

Publishing

unsupported_platform422
The endpoint does not serve that platform — listing posts covers Instagram, Facebook, TikTok, YouTube, and X, and messages are Instagram, Facebook, or WhatsApp only.
unsupported_feature422
Scheduling, queueing, and background uploads are deferred. scheduled_date, add_to_queue, and async_upload are rejected.
invalid_image422
The image is not decodable, not a supported format, or over the size limit.
invalid_video422
The video is not a usable MP4, is over the size limit, or exceeds the creator's maximum duration.
privacy_level_unsupported422
A TikTok video asked for a privacy_level other than PUBLIC_TO_EVERYONE. TikTok videos publish publicly; send post_mode: "MEDIA_UPLOAD" for a draft instead.
invalid_post_settings422
The requested privacy, comment, duet, or stitch setting is not available to this creator, or branded content was requested without public visibility.
media_url_unverified422
TikTok has not verified this deployment's media URL prefix, which it must before any TikTok post works.
upload_limit_exceeded422
The YouTube channel has reached YouTube's own upload limit. Try again later.
provider_rejected422
The provider refused the post's content or metadata, such as a YouTube category_id that does not exist.
duplicate_post409
X refused the post because the account published the same text recently.
invalid_media422
An X media item is not valid base64, does not match its content_type, is over its size limit, or X could not process it.

Comments

post_not_found404
The postId is not one of this account's posts.
comment_not_owned422
TikTok deletes only comments the account itself wrote. Hide other people's comments instead.
not_supported422
The platform cannot do this: liking a comment outside TikTok, or cursor paging outside TikTok and YouTube.

Upstream

provider_error502
The provider request failed. The upstream body is deliberately not passed through.
rate_limited429
TikTok is throttling this account (40 requests a minute per endpoint) or the app, or YouTube is throttling per-second. Retry with backoff.
quota_exhausted429
YouTube's daily API quota, shared by every Adeli customer, is used up. The message says when it resets, at midnight Pacific; retrying before then fails.
provider_unavailable502
Every attempted provider failed for mixed reasons. details carries the per-account errors.
temporarily_unavailable503
X is unavailable to Adeli for a while. Retry later.
database_error502
Adeli could not read or write its own store. Safe to retry.
result_limit—
Appears inside an aggregate errors array when a collection was truncated at 10,000 records, which makes the response 207.

Handling errors

Branch on code, not on message, and treat 207 as its own case.

const response = await fetch(`${BASE}/accounts`, { headers });
const body = await response.json();

if (!response.ok) {
switch (body.error.code) {
  case "unauthorized":
    throw new Error("Adeli key is invalid or revoked");
  case "connection_expired":
    return promptCustomerToReconnect();
  default:
    throw new Error(`${body.error.code}: ${body.error.message}`);
}
}

// 200 and 207 both land here.
if (body.status === "partial") {
console.warn("Incomplete result", body.errors);
}

return body.accounts;

Retry 502, 429, and database_error with backoff. Do not retry 4xx — the request will fail the same way — and be careful retrying a publish that timed out, since there is no idempotency key.