Layers
Partner APIConcepts

Social accounts

Connected platforms (TikTok, Instagram), plus leased TikTok accounts provisioned by Layers.

View as Markdown

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 then appears 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/v2/auth/authorize/?...",
  "state": "st_01HX9Y6K7EJ4T2ABCDEFHX9Y6K7EJ4T2ABCDEF",
  "expiresAt": "2026-04-18T12:30:00Z"
}

Redirect the user to authorizeUrl. When they finish, Layers redirects them back to your returnUrl with no extra query params on success — only failure paths append ?layers_oauth_error=<code>. Detect completion by polling GET /v1/social/oauth-status/:state 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.

What happens after a successful connect

A successful OAuth callback does more than write a row to social_accounts. The following side effects fire automatically and are required for the account to be publishable:

  • Profile picture is rehosted to a Layers-managed CDN. The platform-CDN URL (Instagram especially) rotates within hours; Layers stores a stable URL on the social account so partner UIs don't render broken avatars.
  • Per-account distribution layers are provisioned. Layers initializes the system "Social Account Onboarding" template, which creates four per-account scheduling layers — Account Health Monitor, Social Distribution, Social Engagement, and Account Warmup — and attaches them to the new account. These are required for globalPlatformPostsSync to pick the account up and for scheduled posts targeting this account to publish.
  • Project's Social Content layer is ensured. If the project doesn't already have a Social Content layer, one is created so the new distribution layers have a content source to draw from.
  • Onboarding-task event fires for the org's checklist (tiktok.connected or instagram.connected).

All steps are idempotent and best-effort: a failure on any one of them logs but does not fail the OAuth flow. By the time oauth-status returns completed, the social account is connected and the layer fan-out has been queued. Reauth flows skip these side effects — the layers and onboarding-task event already exist for the account.

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_9c1e42a0...", "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.

Submit a lease request with the target niche and count. Layers reviews the request, assigns warmed accounts when available, and the accounts appear in the social-accounts list.

POST /v1/projects/prj_254a4ce1-f4ca-42b1-9e36-17ca45ef3d39/leased-accounts/request
{
  "projectId": "prj_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: requestedassigned (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 returned 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_9c1e42a0-b7f3-4e5d-a2c1-8b4f5e6c7d8e", "mode": "publish" },
    { "socialAccountId": "sa_4d8a1bf3-2c5e-4769-aa1d-3f5e8c7b9a01", "mode": "draft" }
  ],
  "scheduledFor": "2026-04-20T17:00:00Z"
}

mode is one of publish, draft, or managed. publish auto-posts to the connected platform at scheduledFor. draft pushes the asset to the creator's mobile app (TikTok inbox / Instagram SMS) for a final review before they post by hand. managed dispatches via the project's connected managed-distribution provider. IG placement (reels vs feed) is selected automatically from the container's media_type; there is no feed/reels knob on the partner API.

Platform coverage

Only the platforms below are wired end-to-end (UI + partner API + callback + token exchange). Anything not listed is unsupported — platform values outside this set are rejected with 422 VALIDATION at the /social/oauth-url endpoint.

PlatformConnected (OAuth)Leased
TikTokYesYes
InstagramYes-

LinkedIn and YouTube are not implemented today and are not on a committed timeline. They will appear in this table when they ship; until then, treat them as out of scope for any partner integration plan.

Edge cases worth pinning down

A few situations every partner hits eventually. Working through them up-front saves debugging time later.

Same handle, connected to two projects in the same org

A TikTok account @acmecoffee can be connected to project A and project B independently. Layers represents the platform identity (the TikTok user acmecoffee) once per organization, then issues a distinct socialAccountId per project that binds the platform identity to that project's scheduling. The end-customer goes through OAuth consent twice (once per project), and each call returns a different socialAccountId.

Why it's set up this way: a scheduled post targets a socialAccountId, not a platform handle. Layers needs to attribute scheduled posts, metrics, and revocation actions to a specific project — so each binding is its own row, even when the underlying TikTok account is the same.

If the end-customer asks "why am I being prompted to reconnect TikTok in this second project?" — that's why. There's no shortcut today; the consent has to happen per project.

Revoke → reconnect

Calling DELETE /v1/social-accounts/:id marks the old socialAccountId as disconnected permanently. If the end-customer wants back in, run them through oauth-url again — that creates a new socialAccountId (the old one stays in the system as a soft-deleted audit trail, but never reappears in the list endpoint).

Implications:

  • Historical posts published under the old socialAccountId keep their externalUrl references and stay visible in your historical post lists.
  • Anything you keyed off the old socialAccountId (analytics dashboards, customer-facing UI references) needs to be re-keyed to the new ID.
  • Token rotation under reauth is a different flow — reauth-url preserves the socialAccountId. Only revoke → oauth-url mints a fresh ID.

Same handle, two different organizations

A TikTok user can independently consent to two partner organizations on Layers. The two organizations get completely separate views — distinct socialAccountIds, distinct tokens, distinct project bindings — even though TikTok itself sees the same underlying user. Layers does not deduplicate the platform identity across organizations.

This matters when one of your customers is a partner running their own integration. Your view of @acmecoffee and their view are isolated: separate tokens, separate scheduled posts, separate metrics syncs. The TikTok platform sees both as the same connected app sessions but doesn't expose cross-org visibility to either side.

reauth_required does NOT cancel scheduled posts

When tokens go bad and an account flips to reauth_required, any queued scheduled posts stay queued. They start failing with CREDENTIAL_INVALID at publish time until the partner mints a reauth-url and the user completes consent. Layers does not preemptively cancel — partners often catch reauth windows in time and the scheduled posts succeed on the recovered token.

Only revoke (DELETE) actively cancels queued posts. The cancellation count comes back as canceledScheduledPosts on the DELETE response.

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 canceledScheduledPosts count.
  • 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.

On this page