---
title: "Claim Public Run"
method: POST
path: "/v1/runs/{token}/claim"
tags: ["v1", "public"]
---

# Claim Public Run

`POST /v1/runs/{token}/claim`

## Request

- Base URL: `https://thrustlab.com`
- URL: `https://thrustlab.com/v1/runs/{token}/claim`
- Auth: HTTP bearer

Import an anonymous run into the caller's default project.

Returns ``{simulation_id, project_id, kind}`` — the ids of the saved run, so
the client can navigate straight to it.

A run may be claimed ONCE. A second attempt by the SAME account replays the
first answer (the claim is idempotent for its owner, which is what makes a
double-click harmless); by a different account it is a 409, because the run
belongs to whoever got there first.

THE CLAIM IS A RACE, and it is won by a conditional UPDATE rather than by
reading and then writing. ``SELECT ... FOR UPDATE`` serialises the read, but
it cannot serialise the whole route: ``reserve_credits`` commits partway
through, which ends the transaction the row lock was scoped to. So ownership
is RESERVED - and committed - before the replay runs, and the simulation id
is filled in afterwards.

THE RESERVE GUARD IS STRICT (``claimed_by_user_id IS NULL``), and it has to
be. Admitting "or it is already mine" let two overlapping claims from the
SAME account both pass it while ``claimed_simulation_id`` was still null:
both replayed, both reserved credits, and the second UPDATE orphaned the
first simulation in the user's own project. A reserve that does not match is
therefore re-read rather than assumed lost - if the row already points at a
simulation of the caller's, that is the idempotent double-click and the
existing answer is returned; if it points at nothing, the caller's own claim
is still in flight and this one is a 409 it can retry.

The one case that legitimately re-reserves an already-owned row is a STALE
link: the saved simulation was deleted, so the claim points nowhere. That
reserve matches on the stale id and CLEARS it in the same statement, which is
what keeps two concurrent retries from both winning - the second no longer
matches the id the first has just removed.

## Path parameters

- `token` string, required

## Response `202`

Successful Response

- PublicRunClaim: What ``POST /v1/runs/{token}/claim`` answers with.
  - `kind` 'single' | 'sweep', required: Which create spine the stored body was replayed through.
  - `object` 'public_run_claim'
  - `project_id` string, required: Project the run was filed in — the account's default project (proj_<ksuid>), created on first need.
  - `simulation_id` string, required: Public id of the saved simulation (sim_<ksuid>).

## Other responses

- `422`: Validation Error

---

[API](https://skmtc.dev/thrustlab/apis/thrustlab-api.md) · [All operations](https://skmtc.dev/thrustlab/apis/thrustlab-api/llms.txt) · [OpenAPI document](https://skmtc.dev/thrustlab/apis/thrustlab-api/revisions/c19717e52529?raw)
