Social accounts
Connected platforms (TikTok, Instagram), plus leased TikTok accounts provisioned by Layers.
A social account is any platform handle your project can publish through. There are two kinds and they share one shape: connected accounts are owned by the end-customer and authorized via OAuth, and leased accounts are owned by Layers and bound to your project by Layers. Once either one lands in the project, the API treats them identically — same list endpoint, same scheduled-post target, same revoke semantics.
Connected accounts
The end-customer authorizes their own TikTok or Instagram account via OAuth. You create the URL, they complete the flow on the platform, they land back in your UI. The account row shows up in the project's social-accounts list.
POST /v1/projects/:projectId/social/oauth-url
{
"platform": "tiktok",
"returnUrl": "https://app.your-product.com/connect/complete",
"usageNote": "Connect TikTok to let Acme publish videos to your account."
}
→ 200
{
"authorizeUrl": "https://www.tiktok.com/auth/authorize?...",
"state": "st_01HX9Y6K7EJ4T2ABCDEFHX9Y6K7EJ4T2ABCDEF",
"expiresAt": "2026-04-18T12:30:00Z"
}Redirect the user to authorizeUrl. When they finish, Layers' callback writes the social_accounts row and 302s them back to your returnUrl with ?layers_state=<state>&status=success. You then call GET /v1/social/oauth-status/:state (not project-scoped — the state token itself scopes the lookup) to pick up the new socialAccountId.
returnUrl has to match your API key's allowed-return-domains allowlist exactly. Mismatches get 403 RETURN_URL_NOT_ALLOWED. The allowlist is set when the key is created — contact Layers to add domains.
Reconnecting
Platform tokens expire and platforms invalidate them for reasons outside your control. When a token goes bad, the social account's status flips to reauth_required (the webhook that fires is named social_account.needs_reauth — they describe the same event from two angles). Use POST /v1/projects/:projectId/social/reauth-url with { "socialAccountId": "sa_...", "scopes": ["..."] } in the body to create a reconnection URL — the handle stays the same, only the token rotates.
Leased accounts
Leased accounts are TikTok accounts Layers owns, warms, and rents to your project for distribution. They exist because TikTok distribution works better when content goes out on a warmed account in the right niche than on a cold end-customer account.
The important thing to know up front: provisioning is a manual admin function at Layers, permanently. Your API submits a lease request; Layers reviews the niche, allocates a warmed account, and binds it to your project by inserting a social_accounts row. Your agent then sees it appear in the list call.
POST /v1/projects/254a4ce1-f4ca-42b1-9e36-17ca45ef3d39/leased-accounts/request
{
"projectId": "254a4ce1-f4ca-42b1-9e36-17ca45ef3d39",
"count": 3,
"niche": {
"vertical": "fitness",
"audience": "25-40 women",
"language": "en-US",
"geo": ["US", "CA"]
},
"note": "Product launch the first week of May"
}
→ 202
{
"requestId": "lreq_01HXB2J9FGHZMNOPQRSTUVWX",
"status": "requested",
"submittedAt": "2026-04-18T12:04:11.000Z"
}Request routes are project-scoped. Body projectId must match the path. Poll GET /v1/projects/:projectId/leased-accounts/requests/:requestId for status — or subscribe to the lease_request.assigned webhook to stop polling. States today: requested → assigned (or partial / rejected). Finer-grained intermediate states (in_review, provisioning, failed) are planned.
Billing
Assigned leased accounts are billed per-account against the org's wallet. The monthly price for each account is set by Layers at assignment time and surfaced on the account row as monthlyPriceCents. Billing begins on assignment, not on request. Releasing an account (DELETE /v1/leased-accounts/:id) stops the next renewal; it doesn't refund the current month.
Publishing through either kind
Once a social account is in the project — however it got there — it's a valid target for scheduling:
POST /v1/content/:containerId/schedule
{
"targets": [
{ "socialAccountId": "sa_01HX9Y6K7EJ4T2ABCDEF...", "mode": "direct_publish" },
{ "socialAccountId": "sa_01HY9Y6K7EJ4T2ABCDEF...", "mode": "draft_to_device" }
],
"scheduledFor": "2026-04-20T17:00:00Z"
}Valid modes depend on the platform. direct_publish is the default; TikTok also supports draft_to_device (creator reviews on the mobile app before posting); Instagram supports reels and feed as shape variants.
Platform coverage
| Platform | Connected (OAuth) | Leased (ops-provisioned) |
|---|---|---|
| TikTok | Yes | Yes |
| Yes | — | |
| Planned | — | |
| YouTube | Planned | — |
LinkedIn and YouTube depend on platform-side approval timelines; timing isn't committed yet.
Revocation
DELETE /v1/social-accounts/:id revokes the account from the project. Effects:
- Token is invalidated at the platform where possible.
- Every queued scheduled post against this account is canceled; the response includes a
canceledScheduledPostscount. - For leased accounts, this is also the release — the account goes back into the Layers pool and stops billing.
- Historical posts (already published) stay in the project; they just won't receive new metrics syncs after token revoke.
There's no soft-delete. If you want to pause publishing without losing the connection, don't revoke — just stop scheduling new posts. The account stays in status: "connected" until it's explicitly revoked or its platform token dies.