# End a session and purge its context

> Marks the session `released` and deletes its stored cookies and storage in the same transaction.

Source: https://docs.spicrawl.com/api-reference/sessions/sessions-release

## POST /v1/sessions/{sessionID}/release

Operation ID: `sessionsRelease`. API key scope: `sessions`.

Marks the session `released` and deletes its stored cookies and storage in the same transaction. The
browser worker's own copy of the session state in object storage is deleted too. The
session row stays readable via `GET /v1/sessions/{id}`; the context does not. This is not
"release the lease, keep the state": the state is destroyed. A later `/v1/scrape` with this
`session_id` is refused with 410 `ERR::SESSION::RELEASED` at 0 credits.

Pitfalls:
- Idempotent: releasing an already-released session returns 200 with the same body.
- Releasing an expired session succeeds and marks it `released`.
- While a render holds the lease, returns 409 `ERR::SESSION::BUSY` with `Retry-After`. Wait, or repeat
  with `force=true` to end the session and fail the running task.
- Takes no body.

### Example

```bash
curl -X POST "https://api.spicrawl.com/v1/sessions/{sessionID}/release" \
  -H "Authorization: Bearer $SPICRAWL_API_KEY"
```

### Parameters

| Name | In | Type | Required | Description |
|---|---|---|---|---|
| `sessionID` | path | string | yes | Session id from `POST /v1/sessions` (26-character ULID; a UUID is also accepted). A malformed id answers 404 `ERR::SESSION::NOT_FOUND`, the same as an unknown one. |
| `force` | query | boolean; default `false` | no | Set `true` to end the session even while a render holds its lease; the running task fails. Parsed as a Go boolean (`true`, `false`, `1`, `0`, `t`, `f`, any case); other values are refused with 400. |

### Responses

#### 200

The session, now `released`, with `context` null.

`application/json`.

| Field | Type | Required | Description |
|---|---|---|---|
| `id` | string | yes | Session id (26-character ULID). Pass it as `session_id` on `/v1/scrape`. |
| `project_id` | string | yes | Project the session belongs to (the creating key's project). |
| `engine` | string: `fetch`, `obscura`, `chromium`, `camoufox` | yes | Engine a session is pinned to for its whole life. Scrapes on the session run on this engine; `fingerprint` is only valid with `camoufox`. |
| `status` | string: `active`, `released`, `expired` | yes | Derived from the clock, not a stored flag. `active` sessions can be used; `released` and `expired` ones have no context and cannot be revived. Create a new session. |
| `sticky_key` | string | yes | Exit pin in use. The lowercase session id when you did not set one; empty string when `rotate_ip` was set. |
| `proxy` | object (`SessionProxy`) | yes | The session's exit intent. Not an endpoint or IP. |
| `created_at` | string (date-time) | yes | Creation time, RFC 3339 UTC. |
| `last_used_at` | string \| null (date-time) | yes | Last time a successful scrape used the session. Null means never used. |
| `expires_at` | string (date-time) | yes | Current expiry. Each successful scrape moves it to that time + the session's `ttl_seconds` (never earlier than it was, never past `hard_expires_at`). After it passes the context is purged. |
| `hard_expires_at` | string (date-time) | no | Fixed ceiling set at creation from the organization's maximum TTL. Plan a re-login before this time. Omitted only for legacy rows. |
| `released_at` | string (date-time) | no | When the session was released. Present only on released sessions. |
| `usage_count` | integer | yes | Number of successful scrapes that have used the session. Failed scrapes and `/context` reads do not count. |
| `context` |  | yes | Size summary of the stored context. Null once the session is not active (the context has been purged). |
| `retired` | string: `expired`, `usage_exhausted`, `explicit` | no | Why a worker took this session out of service. Absent while usable. A retired session should be replaced. |
| `domain_scores` | object | no | Per-domain block evidence the workers maintain. Omitted when empty. |
| `fingerprint` | object | no | Camoufox fingerprint, as stored. Omitted when none was set. |
| `lease` | object (`SessionLease`) | no | Present only while a render holds the single-writer lease. While present, other scrapes, release and delete get 409 `ERR::SESSION::BUSY`. |
| `warnings` | array of string | no | Decisions made on your behalf at creation, e.g. a TTL reduced to the organization maximum. Only on the create response; omitted when empty. |

#### 400

The request is invalid. Not retryable; fix the request using `code` and `diagnostics.hint`.

Headers: `X-Request-Id`.

`application/problem+json` (`Problem` schema).

#### 401

Missing, invalid, revoked or expired key. Not retryable with the same key.

Headers: `X-Request-Id`.

`application/problem+json` (`Problem` schema).

#### 403

The key lacks the scope this route requires (`ERR::AUTH::INSUFFICIENT_SCOPE`), or the action is not permitted.

Headers: `X-Request-Id`.

`application/problem+json` (`Problem` schema).

#### 404

No such resource for this key's organization.

Headers: `X-Request-Id`.

`application/problem+json` (`Problem` schema).

#### 409

Conflicts with the resource's current state, for example a session in use (`ERR::SESSION::BUSY`, retryable).

Headers: `X-Request-Id`.

`application/problem+json` (`Problem` schema).

#### 500

Internal error. Retryable when `retryable` is true.

Headers: `X-Request-Id`.

`application/problem+json` (`Problem` schema).

Full OpenAPI spec: https://docs.spicrawl.com/openapi.yaml
