Errors
Every failure returns JSON in the same shape, whatever went wrong.
{
"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
| Status | When |
|---|---|
200 | Success, or an aggregate read where every provider succeeded. |
201 | A resource was created — an Instagram post, a message, a connection session. |
202 | A TikTok or YouTube publish, or a Facebook video, was accepted. It is not published yet; poll for status. |
207 | An aggregate read where some providers succeeded and some failed. |
400 | The request is malformed: bad JSON, a failed schema check, a non-UUID identifier. |
402 | X is not enabled for your account: x_billing_required. See Usage. |
403 | Only from POST /api/v1/profiles. Scoped keys cannot create profiles. |
404 | The resource does not exist, or it exists but your key cannot reach it. |
413 | The request body is too large. |
415 | Content-Type is neither application/json nor multipart/form-data. |
422 | The request is well-formed but the operation is not possible: an unsupported platform, an expired connection, invalid media, a deferred feature. |
429 | The 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. |
502 | Every attempted provider failed, or a database read failed. Provider details are sanitized away. |
503 | X 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
profileIddoes 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
metadataover 16 KiB. invalid_redirect_uri400- The connect
redirectUrlis not a valid URL, its origin is not allowlisted, or it is not HTTPS outside localhost. unsupported_media_type415Content-Typemust beapplication/jsonormultipart/form-data.
Conflicts
profile_name_conflict409- Another profile already uses this name.
details.existingProfileIdnames it. profile_external_id_conflict409- Another profile already uses this
externalId.details.existingProfileIdnames 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.missingScopeslists 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, andasync_uploadare 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_levelother thanPUBLIC_TO_EVERYONE. TikTok videos publish publicly; sendpost_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
postIdis 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
cursorpaging 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.
detailscarries 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
errorsarray when a collection was truncated at 10,000 records, which makes the response207.
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.