Layers

GET /v1/social/oauth-status/:state

Poll the state of a pending OAuth handshake by state token.

View as Markdown
GET/v1/social/oauth-status/:state
Phase 1stable
Auth
Bearer
Scope
social:read

Poll this endpoint with the state you got back from POST /v1/projects/:id/social/oauth-url to learn whether the end-customer completed consent and, if so, which socialAccountId was created.

The endpoint is not project-scoped — the state token uniquely identifies the attempt, and Layers scopes results by the API key that created it (a state created under key A is invisible to key B).

Poll at 2-second intervals with jitter for up to 10 minutes. After that, the state expires and you create a new URL. Don't poll faster than once per second per state.

Path
  • state
    stringrequired
    Opaque state token from oauth-url. Case-sensitive.

Example request

curl "https://api.layers.com/v1/social/oauth-status/st_01HXZ8K2M4P5QRS6TUV7WXYZ9A" \
  -H "Authorization: Bearer lp_live_01HX9Y6K7EJ4T2_4QZpN..."
async function waitForOAuth(state: string) {
  const deadline = Date.now() + 10 * 60 * 1000;
  while (Date.now() < deadline) {
    const status = await layers.social.getOAuthStatus({ state });
    if (status.status === "completed") return status.socialAccountId;
    if (status.status === "failed") throw new Error(status.error?.message ?? "OAuth failed");
    await new Promise((r) => setTimeout(r, 2000 + Math.random() * 500));
  }
  throw new Error("OAuth timed out");
}
import time, random

def wait_for_oauth(state: str) -> str:
    deadline = time.time() + 600
    while time.time() < deadline:
        status = layers.social.get_oauth_status(state=state)
        if status["status"] == "completed":
            return status["socialAccountId"]
        if status["status"] == "failed":
            raise RuntimeError(status.get("error", {}).get("message", "OAuth failed"))
        time.sleep(2 + random.random() * 0.5)
    raise TimeoutError("OAuth timed out")

Response

200Pending — keep polling
{
  "state": "st_01HXZ8K2M4P5QRS6TUV7WXYZ9A",
  "status": "pending",
  "expiresAt": "2026-04-18T19:12:11Z"
}
200Completed — account connected
{
  "state": "st_01HXZ8K2M4P5QRS6TUV7WXYZ9A",
  "status": "completed",
  "socialAccountId": "sa_01HXZ9P2M4N5KLM6TUV7WXYZ9A",
  "platform": "tiktok",
  "handle": "acmecoffee",
  "connectedAt": "2026-04-18T19:06:42Z"
}
200Failed — user denied or platform rejected
{
  "state": "st_01HXZ8K2M4P5QRS6TUV7WXYZ9A",
  "status": "failed",
  "error": {
    "code": "USER_DENIED",
    "message": "The user did not grant consent on the platform."
  }
}

Errors

StatusCodeWhen
401UNAUTHENTICATEDMissing or invalid key.
403FORBIDDEN_SCOPEKey lacks social:read.
404NOT_FOUNDstate unknown, expired, or created under a different project.
429RATE_LIMITEDPolling budget exhausted — back off with the Retry-After header.

See also

On this page