Core concepts

Four ideas explain most of the API's behavior: profiles, accounts, the partial-response contract, and the limits on aggregate reads.

Profiles

A profile is a client boundary — one of your end customers or brands. It owns the provider connections made on its behalf. Profiles live in your organization, the workspace your team shares, and an API key belongs to the organization, so one key can act on every profile in it.

Profile

iduuid
Adeli's identifier. Use it in every path that takes a profileId.
namestring
Display name, unique within your organization. 1–200 characters.
externalIdstring | null
Your own identifier for this customer, so you do not have to store a mapping. Unique within your organization when set.
metadataobject | null
Arbitrary JSON you control, up to 16 KiB encoded as UTF-8.
isDefaultboolean
Whether this is the profile new dashboard sessions start on.

Profiles are created in the dashboard or with POST /api/v1/profiles, and listed with GET /api/v1/profiles. Deleting one cascades: its connected accounts and cached history go with it. API keys belong to the organization and are not affected.

Accounts

An account is one provider connection inside a profile — an Instagram professional account, a Facebook Page, a TikTok account, or a YouTube channel. A profile holds at most one of each.

Every endpoint that acts on a provider takes accountId, which is Adeli's UUID. The provider's own identifier comes back as providerId and is informational; passing it where an accountId is expected returns 400 invalid_request.

Account

accountIduuid
The identifier to use in requests. id is the same value.
platform"instagram" | "tiktok" | "facebook" | "youtube"
Which provider this connection belongs to.
providerIdstring
The provider's identifier — an Instagram user id, TikTok's app-scoped open_id, or a YouTube channel id.
displayNamestring
Human-readable name, such as "@adeli".
displayIdentifierstring
The handle or open_id, without decoration.
connectionStatus"connected" | "expired" | "reconnect_required"
Whether Adeli can still act on this account. Anything but connected means the customer has to authorize again.

Instagram long-lived tokens last roughly 60 days. Adeli refreshes them, and POST /api/v1/profiles/:profileId/accounts/refresh forces the attempt, but a connection that has lapsed needs the customer to go back through the connect flow.

Scoping a request

Every request names the profile it acts on, and nothing defaults to one.

  • Pass profileId in the path, the query string, or the body.
  • Or pass accountId. An account belongs to exactly one profile, so it names that profile and profileId can be left out. If you pass both, they must agree, or you get 404 account_not_found.
  • Pass neither and you get 400 profile_required.
  • Pass a profile or account in another organization and you get 404 profile_not_found or 404 account_not_found, whether or not it exists.

Only GET and POST /api/v1/profiles and GET /api/v1/usage/x act on the whole organization and take no profile.

Add accountId to narrow a read to one connection, and platform to narrow it to one provider.

Partial responses

Aggregate reads — accounts, posts, messages — can touch more than one provider in a single request. One provider failing does not throw away the data from the others.

HTTPstatusMeaning
200completeEvery attempted provider succeeded. errors is empty.
207partialAt least one succeeded and at least one failed. Successful records are still in the response; errors says which account failed and why.
404 / 422—Every attempted provider failed for the same reason, and that reason has a meaningful status: account_not_found is 404, connection_expired and provider_not_configured are 422.
502—Every attempted provider failed for mixed or upstream reasons.

A partial response looks like this:

json
{
  "status": "partial",
  "messages": [],
  "errors": [
    {
      "platform": "instagram",
      "accountId": "00000000-0000-0000-0000-000000000000",
      "code": "provider_error",
      "message": "The provider request failed"
    }
  ]
}

Treat 207 as success with caveats

A client that only checks response.ok will silently accept 207 and act on an incomplete list. Check status and inspect errors before you treat an aggregate read as the whole picture.

Provider error bodies are never passed through. An upstream failure becomes a sanitized provider_error so that provider credentials and internal URLs cannot leak into your logs.

Limits

What applies to every request

Paginationnone
Adeli follows each provider's cursors server-side and returns one flat array. There are no cursors in the public API. Filter by platform and accountId to keep responses small.
Result cap10,000
The maximum number of records in one collection response. Exceeding it truncates the array and adds a result_limit entry to errors, which makes the response 207.
Rate limitingnone
Not implemented in v1. Do not rely on its absence — be reasonable, and expect limits to arrive before general availability.
Cachingnone
Every response carries cache-control: no-store. Reads hit the provider.
Instagram image8 MiB
Decoded size for a single image or each carousel entry. JPEG, PNG, and WebP are accepted and normalized to JPEG.
TikTok video64 MiB / 1 GB
64 MiB decoded for base64 JSON bodies; 1 GB for multipart uploads and public URL pulls.
TikTok photo20 MB
Per photo, up to 35 photos, normalized to JPEG at 1080×1920.
YouTube video1 GB / 4 GiB
1 GB for a public video.url pull; 4 GiB for a multipart upload to POST /api/v1/posts/youtube. Base64 JSON bodies are capped by the request size.
YouTube quota10,000 units/day
YouTube's daily quota belongs to Adeli's Google Cloud project and is shared by every connected channel. Reads cost 1 unit, every comment write and video edit 50, and uploads draw on a separate allowance of 100 a day. When the day's quota is spent, YouTube calls return 429 quota_exhausted with the reset time, midnight Pacific.

Idempotency and retries

There is no idempotency key on the public API. POST /api/v1/posts and POST /api/v1/messages are not safe to retry blindly — a retry after a timeout may publish twice. If a publish times out, list posts or query the TikTok or YouTube status endpoint to find out what actually happened before trying again.

Reads are safe to retry.

What is not here

  • Post editing and deletion. PUT and DELETE on posts are deliberately not part of v1.
  • Outbound webhooks. Adeli does not call your server. Poll instead.
  • WhatsApp free-form replies and connecting a number. The API sends WhatsApp templates; see Messages.
  • Comments. Not exposed through the API.
  • Scheduling. scheduled_date, add_to_queue, and async_upload are rejected with 422 unsupported_feature.