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.
idis 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
profileIdin the path, the query string, or the body. - Or pass
accountId. An account belongs to exactly one profile, so it names that profile andprofileIdcan be left out. If you pass both, they must agree, or you get404 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_foundor404 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.
| HTTP | status | Meaning |
|---|---|---|
200 | complete | Every attempted provider succeeded. errors is empty. |
207 | partial | At 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:
{
"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
platformandaccountIdto keep responses small. Result cap10,000- The maximum number of records in one collection response. Exceeding it truncates the array and adds a
result_limitentry toerrors, which makes the response207. 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.urlpull; 4 GiB for a multipart upload toPOST /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_exhaustedwith 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.
PUTandDELETEon 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, andasync_uploadare rejected with422 unsupported_feature.