The API

Everything Playcode can do is an operation in one GraphQL API: the app, Playcode's agent, the CLI, MCP and the SDK all call it. Send a query to https://playcode.io/api/graphql with an access token, and the answer can do only what the token allows.

On this page

Call it

#
curl https://playcode.io/api/graphql \
  -H 'Authorization: Bearer pc_live_...' \
  -H 'Content-Type: application/json' \
  -H 'x-playcode-via: api' \
  -d '{"query":"query { projectSearch(first: 5) { edges { node { uid name title projectType } } } }"}'
Part Value
Endpoint POST https://playcode.io/api/graphql
Credential Authorization: Bearer with an access token (pc_live_...), or the token of a tool signed in through Sign in with Playcode. See Access tokens and permissions
x-playcode-via Optional: api names your door in the workspace's activity
Idempotency-Key Optional, on a write: see Idempotency

How operations are named

#

An operation is a resource and a verb: projectGet, domainUpdate, projectPublishCreate.

Shape Rule Examples
The five verbs Create, Get, Search (a list), Update, Delete projectGet, dnsZoneUpdate
Actions One consequential thing to one resource domainConnect, domainMove
Jobs Anything that can take longer: Create starts it, Get reads its status, Cancel stops it projectPublishCreate, commandRunCreate
Settings Fields of Update, never a verb of their own domainUpdate with autoRenew

Every operation has an effect: READ changes nothing, WRITE changes something, MONEY charges the workspace.

Find an operation

#

The registry is in the API itself, free for every token:

Operation What it answers
apiOperationSearch(query, family) The best matches for words like "buy a domain": each with its effect, whether it needs consent, the permission it needs and its arguments
apiOperationGet(name) One operation: its arguments as a JSON schema, an example, what it returns, and a ready query document

The CLI shows the same: playcode <family> lists a family, and playcode <family> <operation> --help prints the arguments. Over MCP, the tools are search_operations and describe_operation.

Family What it holds Permission a token needs
projects Projects, files, commands, publishes projects:read to read; projects:vm to change
domains Names to buy, domains, DNS zones, orders domains:read to read; domains:write to change
chats Chats with Playcode's agent in a project chats:read to read; chats:write to write
events The workspace's activity None
consent Proposals None
api The registry None
tokens, webhooks Access tokens, webhook endpoints A signed-in person only, never a token
apps An app's email, secrets and services Not reachable with a token today

Errors

#

A refusal comes back in errors, each with extensions.code from a fixed list, a reason, and metadata:

{ "errors": [ { "message": "...", "extensions": { "code": "missing_scope", "reason": "domainUpdate needs the domains:write permission", "metadata": { "needs": "domains:write" } } } ] }
Code Means
invalid_key The token is unknown, revoked or expired
missing_scope The token lacks a permission or does not reach the target. metadata.needs names the permission
access_denied You have no access to this resource
not_found No such resource
validation An argument is wrong; field names it when it can
consent_required The operation needs a proposal: see Consent
proposal_drifted The proposal was made for other arguments
proposal_expired The proposal is too old
proposal_declined The proposal was declined
proposal_consumed The proposal was already used
idempotency_conflict The Idempotency-Key was used with other arguments
idempotency_in_flight The call with this key is still running
rate_limited Too many requests
insufficient_balance The workspace is out of credits
confirmation_required The person must confirm it's them again in the app
killed, disabled Playcode stopped this traffic on purpose
internal Something failed on Playcode's side
refused The service refused; reason says why
#

An operation marked with consent, like buying a domain (domainCreate) or editing DNS records (dnsZoneUpdate), never applies on a bare call. It takes two:

  1. proposalCreate(operation, args) with the operation's exact arguments. It answers the proposal's id and its terms, written by the service that will charge or change.
  2. The operation itself, with the same arguments and proposalId set to that id.

A money proposal lives 15 minutes, any other 1 hour. Changed arguments need a new proposal, and a proposal works once. proposalDecline ends one.

The API does not ask anyone in between. A tool that acts for a person must show them the terms and wait for their yes before the second call: see Docs for agents.

Idempotency

#

Send Idempotency-Key with a write, and the API stores its answer for 24 hours: the same key with the same call answers the stored result instead of running again. The key is 1 to 255 characters of letters, digits, underscore, dot, colon or dash. For an operation that needs consent, the proposal plays this part.

Limits

#
  • A token works in one workspace. A call that names another workspace, or a project outside the token's reach, is refused.
  • Reads do not land in the workspace's activity; every successful write does.
  • Lists are bounded; each list operation's description states its bound.
  • The generated reference of every operation is not published as a docs page yet: use apiOperationSearch and apiOperationGet.