---
name: rhetoric-filing-session-api
description: >-
  How to drive an existing Rhetoric filing session to completion given a
  session token: resuming a session, reading its form, and applying events to
  fill fields and trigger actions. Fetch this file fresh at the start of every
  session rather than caching or hardcoding it; Rhetoric updates it as the API
  evolves.
license: MIT
---

# Rhetoric Filing Session API

A filing session steps a filer through filing a document with a court: a
sequence of forms collecting things like the court, case details, parties,
documents, and payment. This file describes how to drive that session
end to end once you already hold a session token.

This assumes a session already exists and you have been given its
`session_token`. Creating filers and sessions is a separate, backend-only API
that this file does not cover. You can learn more about that process at
https://docs.rhetoric.law.

Retrieve this file at the start of every session instead of storing a copy of it
in code; it is kept up to date as the API evolves, and a saved copy will drift.
Tolerate fields you do not recognize in responses; new ones may appear over
time.

## Routing and authentication

The API is hosted at `https://api.rhetoric.law`. Authenticate using the
session token as a bearer token:

```shell
--header "Authorization: Bearer $SESSION_TOKEN"
```

This token only authenticates the two operations below, scoped to the one
session it was issued for. A missing or invalid token gets a bare 401 either
way, so it cannot be used to probe for sessions it does not hold.

## Operations

Both operations take a JSON body and return a `form` describing the current
step and a `state_version` you must echo back on the next call.

### Resume session — `POST /resume-filing-session`

Fetches the current form for the session without changing anything. Call this
first to see where the session is, and again any time you want to resync without
applying an event.

Request: no input.

Response: `form` (see below), `state_version` (integer).

Errors: `session_expired` — the session can no longer be resumed; this is
terminal, and getting a working session again is out of scope for this file.

```shell
curl -X POST https://api.rhetoric.law/resume-filing-session \
  --header "Authorization: Bearer $SESSION_TOKEN" \
  --header "Content-Type: application/json" \
  --data '{}'
```

### Apply session event — `POST /apply-filing-session-event`

Applies one event to the session and returns the resulting form.

Request: `state_version` (integer, the version you last saw), `event`, one of:

- `{ "kind": "set_field", "field": "<field_key>", "value": <json> }` — sets a
  field's value. Does not by itself advance to the next step.
- `{ "kind": "action", "action_id": "<action_id>" }` — invokes an action,
  such as submitting the current step and advancing.

Response: `form`, `state_version` (the new version — use it for your next
call).

Errors:

- `not_found` — the session no longer exists.
- `session_busy` — another request for this session is in flight; wait
  briefly and retry the same event.
- `stale_state` — `state_version` didn't match the session's current version;
  the error includes a fresh `form` and `state_version` — use those instead of
  retrying blindly, since the field or action you intended may no longer
  apply.
- `session_expired` — terminal, as above.

```shell
curl -X POST https://api.rhetoric.law/apply-filing-session-event \
  --header "Authorization: Bearer $SESSION_TOKEN" \
  --header "Content-Type: application/json" \
  --data '{
    "state_version": 3,
    "event": { "kind": "set_field", "field": "case_category", "value": "civil_unlimited" }
  }'
```

## The form

Each response's `form` describes one step of the session:

- `title` — the step's heading.
- `breadcrumbs` — labels for the steps leading to this one, outermost first,
  current step last.
- `errors` — form-wide error messages not tied to a single field.
- `blocks` — the step's content, in order (see below).
- `actions` — the actions available on this step, each `{ action_id, label,
primary }`. Invoke one with an `action` event to advance. **An empty
  `actions` array means the session is done** — for example, the final
  "Filing submitted" step has no actions and nothing left to do.

### Blocks

- `field_group` — `{ id, title?, fields }`. A group of input fields (see
  below).
- `document_upload` — `{ id, fields }`. One or more document fields, each
  `{ field_key, label, required, accept, value }`, where `accept` is a list of
  accepted file extensions. Set a document field's value with a `set_field`
  event whose `value` is:

  ```json
  {
    "file_name": "complaint.pdf",
    "content_type": "application/pdf",
    "content_base64": "<base64-encoded file bytes>",
    "size_bytes": 12345
  }
  ```

- `summary` — `{ id, title?, items, actions }`. Read-only review content:
  each item is `{ label, value, format? }`, where `format` is one of `money`,
  `date`, or `date_time` and only affects display. May carry its own
  block-scoped `actions` in addition to the form's top-level ones.

### Fields

Each field in a `field_group` has a `field_key` (use this as `field` in a
`set_field` event) and is one of:

- `text` — freeform input. Has `input_type` (`text`, `email`, `password`,
  `tel`), `value_type`, and client-side `validations` (label + regex) you can
  check before sending, though the server re-validates and reports problems
  back on the field or in `errors`.
- `select` — one of a fixed set of `options` (`{ value, label }`). Set the
  field to an option's `value`.
- `checkbox` — boolean.

All three carry `required`, `description`, `value` (current value, if any, in
which case you don't need to re-set it), and `errors` (validation problems, if
any) from the last time this field was touched.

## Typical flow

1. `resume-filing-session` to get the current `form` and `state_version`.
2. For each field you need to fill on this step, send a `set_field` event
   with that field's `field_key` and value, using the returned `state_version`
   each time and updating it from each response.
3. Once the step's required fields are set, send an `action` event with the
   step's primary action's `action_id` (`primary: true`) to advance.
4. On `stale_state`, use the `form`/`state_version` from the error and decide
   whether your pending change still applies. On `session_busy`, retry the
   same event after a short delay.
5. Repeat from step 1 until a response's `form.actions` is empty — the
   session is complete.
