Quickstart
From an API key to an agent's first answer, then to events pushed to your server. You create a
workspace, give it a model key, publish a small agent called hello, run it once, and register a
webhook endpoint. Every step is one or two curl commands.
What you need
- A beta invite code. During the private beta, signing up needs one.
- An Anthropic API key for the workspace. Use a spend-limited Anthropic workspace key: the agent can read it inside its sandbox (step 3 says why).
curl, and for step 6 an HTTPS URL of yours that the internet can reach.
1. Create an API key
Sign up at https://app.forgeling.io/console with GitHub, Google or a verified email, enter your beta invite code and accept the beta terms. Signing up creates your app.
Then request approval in the console. Forgeling approves every app before it can run anything. Until
yours is approved, everything below works except starting a run: POST /v1/workspaces/{ws}/runs
answers 403 approval_required. You can do steps 1 to 4 and step 6 while you wait.
Under Keys, create a key with these scopes:
| Scope | Used for |
|---|---|
workspaces:write |
Creating the workspace (step 2). |
credentials:write |
Storing its model key (step 3). |
bundles:write |
Publishing hello (step 4). |
bundles:read |
Checking that the version is ready (step 4). |
runs:write |
Starting and completing the run (step 5). |
runs:read |
Reading the run back (step 5). |
events:read |
Reading the run's events (step 5). |
webhooks:write |
Registering and testing a webhook endpoint (step 6). |
A write scope does not imply read: a key with runs:write alone can start a run but cannot read it
back. Scopes lists them all.
The key is shown once, when you create it. Store it in your server's secret store, never in a browser, a mobile app or a repository, and put it in your shell for the commands below:
export FORGELING_API_KEY=fgk_…
Every call sends it as a bearer token, -H "Authorization: Bearer $FORGELING_API_KEY".
2. Create a workspace
A workspace holds one of your customers: their model key, their runs and their events. Give it your
own id for that customer as external_id.
curl https://api.forgeling.io/v1/workspaces \
-H "Authorization: Bearer $FORGELING_API_KEY" \
-H "Content-Type: application/json" \
-d '{"external_id": "quickstart", "name": "Quickstart"}'
The answer is the workspace, 201 the first time. Sending the same external_id again answers 200
with the same workspace, so this call is safe to repeat. Keep its id:
export WS=ws_…
A workspace is a record, not a machine: it costs nothing until a run starts in it.
3. Store the model credential
Runs use the workspace's own Anthropic key, never one of Forgeling's. Replace sk-ant-… with the key:
curl -X PUT "https://api.forgeling.io/v1/workspaces/$WS/model_credential" \
-H "Authorization: Bearer $FORGELING_API_KEY" \
-H "Content-Type: application/json" \
-d '{"type": "anthropic_api_key", "api_key": "sk-ant-…"}'
Forgeling checks the key with one small Anthropic call and answers "status": "valid" with its last
four characters. A key Anthropic refuses is 422 credential_invalid. The key itself is never returned.
The key is readable by the agent inside every run sandbox of the workspace, and by any code the agent runs. Use a spend-limited Anthropic workspace key, one per customer, so a leaked key can spend only what you allowed. Your org's Anthropic key on the security page has the details.
4. Publish the hello agent
An agent definition is a named, versioned bundle. The simplest way to publish one is an inline spec: a model, instructions, and the tools the agent may use.
curl https://api.forgeling.io/v1/bundles/hello/versions \
-H "Authorization: Bearer $FORGELING_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"version": "1.0.0",
"spec": {
"model": "claude-sonnet-4-5",
"instructions": "You are a friendly assistant. Answer in one or two sentences.",
"tools": {"allow": []}
}
}'
An empty tools.allow list gives hello no tools: it can only answer. The answer is 202 with
"status": "validating". A few seconds later the version is ready (or rejected, with errors);
check it with GET /v1/bundles/hello/versions/1.0.0. The bundle.version_ready and
bundle.version_rejected events say the same.
Versions are immutable. Publishing 1.0.0 again answers 409 bundle_version_exists; to change the
agent, publish 1.0.1. Agent definitions covers every field.
5. Start a run and read its events
A run is one agent in its own sandbox. Start it with initial_turn, its first input:
curl "https://api.forgeling.io/v1/workspaces/$WS/runs" \
-H "Authorization: Bearer $FORGELING_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: quickstart-hello-1" \
-d '{
"definition": {"bundle": "hello", "version": "1.0.0"},
"size": "small",
"approvals": {"mode": "deny"},
"budget": {"max_cost_usd": 1},
"initial_turn": {
"input": [{"type": "text", "text": "Say hello, and name one thing you can help with."}]
}
}'
sizeis the sandbox's size;smallis plenty for an agent without tools.approvalsset todenyrefuses at once any tool call that the definition does not allow.budgetstops the run's turns once the run has spentmax_cost_usd, here one US dollar.Idempotency-Keyis required on this call. If the answer is lost, send the same request with the same key: you get the same run back, withIdempotent-Replayed: true, never a second run. Idempotency has the rules.
The answer is 202 with the run, "status": "queued", and its first turn as turn. Keep the run's
id:
export RUN=run_…
If the answer is an error instead, its code says why: 403 approval_required until your app is
approved, 422 bundle_version_unavailable while hello@1.0.0 is not ready, or 409
credential_missing without a valid model key.
The run goes through run.queued, run.provisioning, run.ready and run.running, the turn
through turn.started and turn.completed, and then the run waits for more input with run.idle.
Read its events:
curl "https://api.forgeling.io/v1/runs/$RUN/events" \
-H "Authorization: Bearer $FORGELING_API_KEY"
The answer lists the run's events in stream.seq order. Read again until the list ends with
run.idle; starting the sandbox takes a little while. The turn.completed event carries the agent's
answer in data.output.text, and its usage, cost_usd included. GET /v1/runs/{run} shows the run's
status and its usage so far. The event catalog describes every event type.
An idle run can take more turns (POST /v1/runs/{run}/turns). This one is done, so complete it:
curl -X POST "https://api.forgeling.io/v1/runs/$RUN/complete" \
-H "Authorization: Bearer $FORGELING_API_KEY"
Completing tears the sandbox down. The run's last event is run.completed,
with usage_total.
6. Get events pushed to you
Polling works, but a webhook endpoint gets each event as it happens. Register an HTTPS URL of yours:
curl https://api.forgeling.io/v1/webhook_endpoints \
-H "Authorization: Bearer $FORGELING_API_KEY" \
-H "Content-Type: application/json" \
-d '{"url": "https://example.com/webhooks/forgeling"}'
The answer is the endpoint, with its signing secret, secret (whsec_…). The secret is shown once:
store it next to your API key. Keep the endpoint's id, and ask for a test delivery:
export ENDPOINT=whe_…
curl -X POST "https://api.forgeling.io/v1/webhook_endpoints/$ENDPOINT/test" \
-H "Authorization: Bearer $FORGELING_API_KEY"
Your URL receives a signed ping: a POST whose body is one event envelope, with the headers
webhook-id, webhook-timestamp and webhook-signature. Check the signature with the secret before
you trust a delivery; Verify every delivery has a function to copy, in
Python and in TypeScript.
The endpoint gets the default subscription, so the next run you start reports its progress there.
Next
- Coding runs: give an agent a GitHub repository and get a pull request back.
- Agent definitions: models, instructions, tool rules and approvals.
- Webhooks and the event stream: delivery rules, ordering and the live stream.
- Conventions: keys, errors, idempotency, pagination and versioning.
- Limits and the security model.