API v1 · Resources
Projects
A project is a named container for content inside a workspace — typically one personal-brand profile or one company page. Notes, posts, sources, and queue slots all attach to a project.
The API exposes a paginated list of the workspace's projects and a
single-project read — both read-only; mutating projects (creation,
archive, pause) is dashboard-only. For LinkedIn company pages
specifically, GET /v1/company-pages
adds activation status, stored admin-token health, and follower counts
on top of the plain project rows.
GET /v1/projects
List the projects the authenticated workspace owns, ordered by creation time descending.
curl -i 'https://api.scripe.io/v1/projects?limit=50' \
-H "Authorization: Bearer scripe_sk_live_…" \
-H "Scripe-Api-Version: 2026-08-10"Query parameters
| Name | Type | Default | Notes |
|---|---|---|---|
limit | integer | 50 | Page size. Above 200 is clamped silently to 200; a non-positive or non-integer value → 400 bad_pagination. |
cursor | string | — | Opaque cursor from a previous response. See conventions. |
Response
HTTP/1.1 200 OK
Content-Type: application/json
{
"data": [
{
"id": "proj_7c1e5a94b2f80d63",
"name": "Ada Lovelace",
"type": "PERSONAL_BRAND",
"typeLabel": "LinkedIn Account",
"status": "ACTIVE",
"username": "ada-lovelace",
"avatarUrl": "https://cdn.scripe.io/avatars/…",
"isActive": true,
"createdAt": "2026-05-15T08:21:00.000Z"
}
],
"pagination": {
"next_cursor": "eyJjcmVhdGVkQXQiOiIyMDI2L…",
"has_more": true
}
}Semantics
Field types, nullability and the type / typeLabel enums live in the
OpenAPI reference tab (Project). What the schema doesn't tell you:
- There is no
urlfield. The LinkedIn identity lives onusername, and it is a bare slug, never a URL: the author handle for a personal brand or amplifier (resolving tohttps://www.linkedin.com/in/<username>) and the company vanity name for a company page (https://www.linkedin.com/company/<username>). It isnulluntil the project is connected. Build the link yourself; don't split the value on/in/. nameis resolved, not raw: a connected project reports the name LinkedIn holds, falling back to the dashboard label.avatarUrlis a CDN URL,nullbefore the project connects.statusisACTIVEfor every project the API can return today — the column has exactly one value — andisActiveis derived from it. Both are here for forward-compatibility; treat an unknownstatusas active.AMPLIFIERis a livetypealongsidePERSONAL_BRANDandCOMPANY_PAGE, and shares the personal-brand projection.linkedInanswers whether Scripe can publish as this project right now — see the next section.
Internal fields like the workspace's owner user id, billing customer id, and onboarding flags are intentionally not in this response. If you need them, use the dashboard.
linkedIn — the publishing precondition
Every project row carries the answer to "can Scripe post as this account right now, and if not, what has to happen".
"linkedIn": {
"state": "revoked",
"canPublish": false,
"connectionStatus": "REVOKED",
"lastValidatedAt": "2026-06-02T08:11:00.000Z",
"accessTokenExpiresAt": null,
"detail": "Scripe's LinkedIn authorization for this project was revoked on 2026-07-30T08:00:00.000Z: The token used in the request has been revoked by the user. Nothing will publish as this project until someone reconnects LinkedIn in Scripe. Reconnecting cannot be done through the API.",
"reconnectUrl": "https://app.scripe.io/oauth?projectId=…&reconnect=true"
}state | canPublish | Means |
|---|---|---|
connected | true | a token is on file and nothing is wrong |
expiring_soon | true | the access token lapses within 14 days (or already has) and has not been renewed |
degraded | true | recent LinkedIn calls failed; three consecutive failures revoke the connection |
disconnected | false | no usable token — never connected, or the token was cleared |
revoked | false | the authorization was withdrawn |
Branch on canPublish or state, never on connectionStatus.
connectionStatus is the stored health cache written by whatever last
talked to LinkedIn. It reads CONNECTED for accounts holding no access
token at all, and UNKNOWN for accounts that were simply never
connected; state applies token presence first.
Reading a project never probes LinkedIn — this is stored health as of
lastValidatedAt. The live check runs inside the schedule and publish
pre-flights, where a write is about to happen.
A company page has no connection of its own: it publishes through the
personal account it was linked to, so its block — including
reconnectUrl — describes that account. null means the page was never
linked to one, which is itself the answer.
linkedIn cannot be repaired through this API. Reconnecting is an OAuth
flow a human performs in the dashboard; relay reconnectUrl rather than
retrying.
GET /v1/projects/{projectId}
Single project read. Returns the same shape as above, wrapped in a data
field for symmetry with future write endpoints.
curl -i https://api.scripe.io/v1/projects/proj_7c1e5a94b2f80d63 \
-H "Authorization: Bearer scripe_sk_live_…" \
-H "Scripe-Api-Version: 2026-08-10"Response
HTTP/1.1 200 OK
Content-Type: application/json
{
"data": {
"id": "proj_7c1e5a94b2f80d63",
"name": "Ada Lovelace",
"type": "PERSONAL_BRAND",
"typeLabel": "LinkedIn Account",
"status": "ACTIVE",
"username": "ada-lovelace",
"avatarUrl": "https://cdn.scripe.io/avatars/…",
"isActive": true,
"createdAt": "2026-05-15T08:21:00.000Z"
}
}Errors
| Status | Code | When |
|---|---|---|
| 400 | bad_cursor, bad_pagination | Cursor invalid / limit not a positive integer. List endpoint only. |
| 401 | unauthenticated, invalid_token, key_revoked, key_expired | |
| 404 | not_found | Unknown project id, OR a project that exists but belongs to another workspace. We do not distinguish — see conventions. |
| 429 | rate_limited |
Cross-workspace isolation
Every project read is scoped by the API key's workspace. A request for
proj_<other-workspace> returns 404 not_found — never a 403, so
attackers cannot enumerate which project ids exist outside their
workspace.
The same isolation applies to the list endpoint: you only see your own
workspace's projects, and the pagination cursor is bound to that filter.
You cannot inflate the result set by replaying another workspace's
cursor — the embedded (createdAt, id) keyset is workspace-agnostic, so
it just no-ops against your own data.
What's NOT here (yet)
- Filtering by status / type. The list returns all projects, regardless of status. Filter client-side.
- Project metrics. Engagement and analytics live on Analytics.
- Write endpoints. Creating, pausing, or archiving a project is dashboard-only.