> ## Documentation Index
> Fetch the complete documentation index at: https://docs.cardinalgray.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Submit Title Entry

> Send a title entry to the state — mail packet or online portal, driven by the entry's state

Cardinal Gray submits title work to the state on your behalf. Depending on the entry's state, submissions run in one of two modes:

* **`MAIL`** — The API synchronously builds a mail packet (cover sheet, forms, and your uploaded documents merged into a single PDF), returns a presigned `packetUrl`, and tells you the `checkAmount` and `checkPayee` to enclose. You print, sign checks, and mail.
* **`ONLINE_PORTAL`** — The API accepts the submission (`202`) and a background worker drives the state portal. Poll [`GET /title/{id}/submit/{submissionId}`](/api-reference/v1/title_id_submit_submissionId) for status and the confirmation screenshot.

## Managed submission mode

Organizations enrolled in **managed submission** use this endpoint differently: the call is a funding-complete signal, not a packet builder. Send the request with **no body** — no `attachments`, no `scenarioKey`. Cardinal Gray's title operations team selects the filing scenario, assembles the documents, and files with the state on your behalf.

* The API responds `202` with `intakeMode: "MANAGED"` and a durable `submissionId`.
* The submission starts at status `AWAITING_READINESS`; poll [`GET /title/{id}/submit/{submissionId}`](/api-reference/v1/title_id_submit_submissionId) to track it. `method` appears once Cardinal Gray selects the filing path.
* Repeated calls for the same title are **idempotent** — you get the same `submissionId` back, and no duplicate filing work is started.
* The title's `account_status` does not change at intake; it advances only when filing actually occurs.

Managed mode is enabled per organization by Cardinal Gray — contact support to enroll. The sections below apply to self-service organizations only.

## Attachments

Every attachment must include an `s3_key` and a `document_type`. Cardinal Gray issues presigned upload URLs through the dashboard's document upload flow; the returned S3 keys are what you pass here. Supported `document_type` values:

* `VEHICLE_TITLE`, `VEHICLE_TITLE_BACK`
* `DRIVER_LICENSE`
* `SIGNED_POWER_OF_ATTORNEY`
* `LIEN_RELEASE_LETTER`
* `COVER_SHEET`
* `UNKNOWN`

## Scenario key

Each supported state exposes one or more submission scenarios (e.g. `CLEAR_TITLE`, `ACTIVE_LIEN`, `LOST_OR_ILLEGIBLE`, `REFINANCE_SECURED`, `LIEN_ADD`).

* **`ONLINE_PORTAL` states — `scenarioKey` is required.** An omitted or invalid value returns `400` with an `INVALID_SCENARIO_KEY` error listing the valid keys for the state.
* **`MAIL` states — `scenarioKey` is optional.** The API resolves the recommended scenario from the entry's current account data. Pass an explicit `scenarioKey` to override that choice.

Contact Cardinal Gray for the current scenario keys supported per state.

## Response codes

<ResponseField name="200 OK" type="MAIL states">
  Packet generated and staged. Response includes `packetUrl`, `packetS3Key`, `checkAmount`, and `checkPayee`.
</ResponseField>

<ResponseField name="202 Accepted" type="ONLINE_PORTAL states / managed intake">
  Submission queued. Poll `GET /title/{id}/submit/{submissionId}` for `status`, `applicationNumber`, and `confirmationImg`. Managed-mode responses include `intakeMode: "MANAGED"` and no `method`.
</ResponseField>

<ResponseField name="400" type="Bad request">
  Missing `attachments`, an attachment missing `s3_key` or `document_type`, an unsupported state, or an invalid `scenarioKey`.
</ResponseField>

<RequestExample>
  ```bash Online portal (KS) theme={null}
  curl -X POST "https://api.cardinalgray.com/title/123e4567-e89b-12d3-a456-426614174000/submit" \
    -H "Authorization: Bearer YOUR_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "scenarioKey": "CLEAR_TITLE",
      "attachments": [
        {
          "s3_key": "private/acme/entry-abc/title-front.pdf",
          "display_name": "Title front",
          "document_type": "VEHICLE_TITLE"
        },
        {
          "s3_key": "private/acme/entry-abc/title-back.pdf",
          "display_name": "Title back",
          "document_type": "VEHICLE_TITLE_BACK"
        },
        {
          "s3_key": "private/acme/entry-abc/dl.pdf",
          "display_name": "Driver license",
          "document_type": "DRIVER_LICENSE"
        }
      ]
    }'
  ```

  ```bash Managed intake (no body) theme={null}
  curl -X POST "https://api.cardinalgray.com/title/123e4567-e89b-12d3-a456-426614174000/submit" \
    -H "Authorization: Bearer YOUR_API_KEY"
  ```

  ```bash Mail packet (FL) theme={null}
  curl -X POST "https://api.cardinalgray.com/title/123e4567-e89b-12d3-a456-426614174000/submit" \
    -H "Authorization: Bearer YOUR_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "scenarioKey": "LIEN_ADD",
      "attachments": [
        {
          "s3_key": "private/acme/entry-abc/title-front.pdf",
          "document_type": "VEHICLE_TITLE"
        },
        {
          "s3_key": "private/acme/entry-abc/spoa.pdf",
          "document_type": "SIGNED_POWER_OF_ATTORNEY"
        }
      ],
      "coverSheetHints": {
        "county": "Miami-Dade"
      }
    }'
  ```
</RequestExample>

<ResponseExample>
  ```json 202 Online portal theme={null}
  {
    "ok": true,
    "submissionId": "b8f3c1a2-8a4e-4a8b-9b2f-6e5c1b1a0f11",
    "entryId": "123e4567-e89b-12d3-a456-426614174000",
    "state": "KS",
    "method": "ONLINE_PORTAL"
  }
  ```

  ```json 202 Managed intake theme={null}
  {
    "ok": true,
    "submissionId": "b8f3c1a2-8a4e-4a8b-9b2f-6e5c1b1a0f11",
    "entryId": "123e4567-e89b-12d3-a456-426614174000",
    "state": "TX",
    "intakeMode": "MANAGED"
  }
  ```

  ```json 200 Mail packet theme={null}
  {
    "ok": true,
    "submissionId": "b8f3c1a2-8a4e-4a8b-9b2f-6e5c1b1a0f11",
    "entryId": "123e4567-e89b-12d3-a456-426614174000",
    "state": "FL",
    "method": "MAIL",
    "packetUrl": "https://s3.amazonaws.com/...&X-Amz-Signature=...",
    "packetS3Key": "private/acme/entry-abc/submit_packet.pdf",
    "checkAmount": 79.75,
    "checkPayee": "Florida Department of Highway Safety and Motor Vehicles"
  }
  ```
</ResponseExample>


## OpenAPI

````yaml POST /title/{id}/submit
openapi: 3.0.1
info:
  title: Cardinal Gray OpenAPI
  description: OpenAPI for automating vehicle title work
  license:
    name: MIT
  version: 1.0.0
servers:
  - url: https://api.cardinalzyn.com
    description: Staging
  - url: https://api.cardinalgray.com
    description: Production
security:
  - bearerAuth: []
  - basicAuth: []
  - cookieAuth: []
paths:
  /title/{id}/submit:
    post:
      summary: Submit a title entry to the state
      description: >-
        Submit a title entry to the DMV — either by mailing a physical packet
        (`MAIL` states) or by driving an online portal (`ONLINE_PORTAL` states).
        Which method is used, which supporting documents are required, and how
        the API responds are all determined by the entry's state.


        Every attachment must include an `s3_key` (the S3 object key from
        Cardinal Gray's upload flow) and a `document_type`. `MAIL` states
        respond `200` with a presigned packet URL and the `checkAmount` /
        `checkPayee` to enclose. `ONLINE_PORTAL` states respond `202` — the
        submission runs in a background worker; poll `GET
        /title/{id}/submit/{submissionId}` for `status`, `applicationNumber`,
        and `confirmationImg`.


        **Managed submission mode.** Organizations enrolled in managed
        submission call this endpoint with **no request body** — the call is a
        signal that the loan is funded and Cardinal Gray should begin
        lien-perfection work. The API responds `202` with `intakeMode:
        "MANAGED"` and a durable `submissionId` whose status starts at
        `AWAITING_READINESS`; Cardinal Gray's title operations team selects the
        filing scenario, assembles documents, and files with the state. Repeated
        calls for the same title are idempotent and return the same
        `submissionId`. No `attachments` or `scenarioKey` are required, and the
        title's `account_status` does not change at intake — it advances only
        when filing actually occurs. Managed mode is enabled per organization by
        Cardinal Gray; contact support to enroll.
      parameters:
        - $ref: '#/components/parameters/id'
      requestBody:
        required: false
        description: >-
          Required for self-service organizations (`attachments` must be
          present). Managed-mode organizations omit the body entirely.
        content:
          application/json:
            schema:
              type: object
              properties:
                attachments:
                  type: array
                  minItems: 1
                  description: >-
                    Supporting documents included with the submission (title
                    images, driver's license, signed power of attorney, lien
                    release letter, etc.). Every item must have `s3_key` and
                    `document_type`. **Required for self-service organizations;
                    omitted in managed mode.**
                  items:
                    type: object
                    required:
                      - s3_key
                      - document_type
                    properties:
                      s3_key:
                        type: string
                        description: S3 key of the uploaded document.
                      display_name:
                        type: string
                        description: >-
                          Human-readable filename shown in the packet and
                          follow-up emails.
                      document_type:
                        type: string
                        description: Document classification.
                        enum:
                          - VEHICLE_TITLE
                          - VEHICLE_TITLE_BACK
                          - DRIVER_LICENSE
                          - SIGNED_POWER_OF_ATTORNEY
                          - LIEN_RELEASE_LETTER
                          - COVER_SHEET
                          - UNKNOWN
                      json_path:
                        type: string
                        description: >-
                          Optional S3 key of the parsed JSON payload for this
                          document.
                scenarioKey:
                  type: string
                  description: >-
                    Submission scenario for this state (e.g. `CLEAR_TITLE`,
                    `ACTIVE_LIEN`, `LOST_OR_ILLEGIBLE`, `REFINANCE_SECURED`,
                    `LIEN_ADD`).


                    **Required for `ONLINE_PORTAL` states** — an omitted or
                    invalid `scenarioKey` returns `400 INVALID_SCENARIO_KEY`
                    with the list of valid values for the state. Optional for
                    `MAIL` states, where the API resolves the recommended
                    scenario from the current account data.
                coverSheetHints:
                  type: object
                  description: >-
                    MAIL only. Overrides for the cover-sheet fields the API
                    derives from the entry (owner list, borrower/lienholder
                    names, ownership form, county).
                  properties:
                    ownerNames:
                      type: array
                      items:
                        type: string
                    borrowerName:
                      type: string
                    lienholderName:
                      type: string
                    isAndTitle:
                      type: boolean
                      description: >-
                        True when the title is joint (AND) rather than either-or
                        (OR).
                    ownerCount:
                      type: integer
                    county:
                      type: string
                options:
                  type: object
                  description: Advanced overrides applied to generated forms in the packet.
                  properties:
                    formOverrides:
                      type: object
                      properties:
                        security_agreement_sign_date:
                          type: string
                          description: >-
                            Override date printed on the security agreement (ISO
                            8601 or `MM/DD/YYYY`).
                        check_number:
                          type: string
                          description: Check number to print on the cover sheet.
      responses:
        '200':
          description: >-
            Submission completed synchronously. Returned for `MAIL` states — the
            packet PDF has been generated and staged.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SubmitSuccess'
        '202':
          description: >-
            Submission accepted and queued. Returned for `ONLINE_PORTAL` states
            — a worker will drive the state portal in the background — and for
            managed-mode intake, where the response includes `intakeMode:
            "MANAGED"` and the submission starts at `AWAITING_READINESS`. Poll
            `GET /title/{id}/submit/{submissionId}` for status.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SubmitSuccess'
        '400':
          description: >-
            Missing or invalid attachments, unsupported state, or invalid
            `scenarioKey`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Response'
        '403':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '500':
          $ref: '#/components/responses/InternalServerError'
      security:
        - bearerAuth: []
        - basicAuth: []
        - cookieAuth: []
components:
  parameters:
    id:
      name: id
      in: path
      required: true
      schema:
        type: string
      description: Unique identifier of the title entry
  schemas:
    SubmitSuccess:
      type: object
      required:
        - ok
        - submissionId
        - entryId
        - state
      properties:
        ok:
          type: boolean
          enum:
            - true
        submissionId:
          type: string
          format: uuid
        entryId:
          type: string
          format: uuid
        state:
          type: string
          description: Two-letter state code the submission targets (e.g. `KS`, `FL`).
        method:
          $ref: '#/components/schemas/SubmissionMethod'
        intakeMode:
          type: string
          enum:
            - MANAGED
          description: >-
            Present when the organization uses managed submission mode. `method`
            is absent on managed intake responses — Cardinal Gray selects the
            filing path during fulfillment.
        confirmationImg:
          type: string
          format: uri
          description: >-
            Presigned URL to a PNG confirmation screenshot captured from the
            state portal. `ONLINE_PORTAL` only.
        applicationNumber:
          type: string
          description: >-
            Application/tracking number returned by the state portal, when
            available.
        packetUrl:
          type: string
          format: uri
          description: Presigned URL to the generated mail packet PDF. `MAIL` only.
        packetS3Key:
          type: string
          description: S3 key for the mail packet PDF. `MAIL` only.
        checkAmount:
          type: number
          description: Dollar amount to write on the enclosed check. `MAIL` only.
        checkPayee:
          type: string
          description: Payee for the enclosed check. `MAIL` only.
    Response:
      type: object
      properties:
        error:
          type: string
          description: Error message
        statusCode:
          type: number
          description: >-
            HTTP status code. Present on selected success responses, such as
            DELETE /title/{id}.
        disallowedFields:
          type: array
          items:
            type: string
          description: >-
            Fields rejected by PATCH /title/{id} when attempting to modify
            restricted top-level fields
    SubmissionMethod:
      type: string
      enum:
        - MAIL
        - ONLINE_PORTAL
      description: >-
        How Cardinal Gray delivers the submission to the state. `MAIL` states
        return a packet PDF for the user to print and mail; `ONLINE_PORTAL`
        states are driven programmatically by a background worker.
  responses:
    Unauthorized:
      description: Unauthorized
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Response'
    NotFound:
      description: Not found
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Response'
    InternalServerError:
      description: Internal server error
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Response'
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      description: 'API key or Clerk JWT supplied as Authorization: Bearer <token>'
    basicAuth:
      type: http
      scheme: basic
      description: >-
        Legacy API key authentication supplied with Authorization: Basic
        <api_key>
    cookieAuth:
      type: apiKey
      in: cookie
      name: __session
      description: Clerk session cookie authentication

````