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

# This tenant's jobs, newest first

> Id, status, source, timestamps, error and intake; fetch a job for its result. Filter by `status`, `source`, `since` (ISO 8601), `end_user_id` and `external_business_id`; page with `cursor` (the previous page's `next_cursor`, with the same filters). `finished_since` lists finished jobs instead, oldest finish first: the polling fallback for missed webhooks, which returns each finished job once across its pages.



## OpenAPI

````yaml /openapi.json get /v1/jobs
openapi: 3.1.0
info:
  title: parser_backend
  summary: >-
    MCA packet parsing with GPT-6 Luna (OpenRouter), verified ledgers and
    underwriting metrics
  description: >

    MCA document parsing: merchant packets in (upload or email), verified
    bank-statement ledgers, ISO

    applications and underwriting metrics out.


    **How a packet is read**


    1. *Intake* - files validated by content (PDF, PNG, JPEG), PDF
    safety-parsed, images converted to PDF;
       email attachments extracted from every MIME part.
    2. *Split* - each PDF is split into documents (bank statement, merchant
    application, tax return, ...) by
       per-page classification and statement boundaries.
    3. *Read* - a statement with a text layer is read by the **layout engine**:
    tables rebuilt from the text
       layer's geometry become the ledger JSON, with no model call. Pages without a text layer, and any statement
       whose layout read does not pass the gate, are rendered at 300 DPI and read by **GPT-6 Luna** through
       OpenRouter with a strict JSON schema at full image resolution (Luna's answer is the OCR): one owned page per
       call, neighbours as context, fanned out in parallel; the same answer categorizes every row. An image-read
       ledger is accepted only when a second, independent image read agrees on every amount, date and balance.
    4. *Gate* - a ledger is **verified** only when it reconciles to the cent
       (`opening + credits - debits = closing`), reproduces the bank's printed totals and keeps every running
       balance chain. If it still fails after the image read it is returned as `review_required`, never as data.
    5. *Categorize* - a category and counterparty per row: revenue, MCA funding
    / payment, loan, NSF, overdraft,
       returned item, transfers, payroll, taxes, ... or unknown. Image reads return them with the OCR; a
       layout-engine ledger goes to GPT-6 Luna once as JSON (rows grouped by description, with counts, amounts and
       dates). Deterministic rules cross-check every row.
    6. *Aggregate* - accounts and statement months (missing months flagged); MCA
    and loan positions decided by
       GPT-6 Luna over the packet's possible financing streams and checked against the rows (cadence
       cross-checks); monthly summaries, and the withhold, EBITDA and revenue math.
    7. *Packet* - the files are one submission (`result.submission_packet`): its
    typed documents in order, which
       ones the decision rests on and why the others are excluded (duplicates, failed or unreconciled reads,
       unclassified documents, another business's statement), the merchant's details with their sources,
       month-by-month coverage, whether the statements are one unbroken history, the balance chain across
       statements, undisclosed accounts and cross-document fraud signals.

    Every Luna call is metered (prompt, cached, completion and reasoning tokens;
    provider-reported cost) and

    every answer is replay-cached: a re-sent document or window costs nothing.


    **Integrating**


    * *Authentication*: `Authorization: Bearer <credential>` (or `X-API-Key`) on
    every `/v1` route. The credential
      is an API key (`pk_live_...`, created with `POST /v1/api-keys`, scoped to permissions) or a WorkOS AuthKit
      access token (a person, with the permissions of their role). Each belongs to one tenant; jobs, caches,
      webhooks and settings are tenant-scoped. Errors are `{"error": {"code", "message", "request_id"}}`.
    * *Submit*: `POST /v1/parse` (multipart) or `POST /v1/intake/email` (raw
    MIME). The answer is `202` with a
      `job_id` and `status_url`, or the finished job when `wait` seconds suffice. Send an `Idempotency-Key` to
      make a retry return the same job.
    * *Get the result*: register a webhook once (`POST /v1/webhooks`) and
    receive `job.completed`,
      `job.review_required` or `job.failed` when each job ends, signed with the endpoint's secret; then fetch
      `GET /v1/jobs/{job_id}`. Or poll that route (`?wait=` long-polls). Every job ends in one of those three
      states, and its event is stored with it: a job that runs past `JOB_TIMEOUT_SECONDS`, or that the service
      stopped during too many runs, fails with its `job.failed` event.
    * *Status*: `completed` - every check passed; `review_required` - the result
    is usable but
      `review_reasons` name what a person should look at; `failed` - nothing could be processed (`error`).
  version: 0.1.0
servers:
  - url: https://api.parser-app.com
security: []
tags:
  - name: parse
    description: Submit documents for parsing
  - name: email
    description: Submit a raw email; its attachments are parsed as one packet
  - name: jobs
    description: Poll jobs and read results
  - name: underwriting
    description: Withhold, EBITDA and capacity calculator
  - name: webhooks
    description: Signed job events pushed to your endpoint, retried until delivered
  - name: usage
    description: Token usage, cost and service metrics
  - name: identity
    description: 'Who is calling: tenant, permissions, modules'
  - name: tenant
    description: This tenant's settings
  - name: api-keys
    description: 'Scoped API keys: create, rotate, revoke'
  - name: mailboxes
    description: Forwarding addresses and Gmail mailboxes, and each message's outcome
  - name: emails
    description: 'Every inbound email tracked: its thread, raw .eml, tags and erasure'
  - name: integrations
    description: Connections to the tenant's CRM and other systems, fed by job events
  - name: audit
    description: Every change made in the tenant
  - name: platform
    description: Tenants across the platform (platform:admin)
  - name: health
    description: Liveness and readiness
paths:
  /v1/jobs:
    get:
      tags:
        - jobs
      summary: This tenant's jobs, newest first
      description: >-
        Id, status, source, timestamps, error and intake; fetch a job for its
        result. Filter by `status`, `source`, `since` (ISO 8601), `end_user_id`
        and `external_business_id`; page with `cursor` (the previous page's
        `next_cursor`, with the same filters). `finished_since` lists finished
        jobs instead, oldest finish first: the polling fallback for missed
        webhooks, which returns each finished job once across its pages.
      operationId: list_jobs_v1_jobs_get
      parameters:
        - name: limit
          in: query
          required: false
          schema:
            type: integer
            maximum: 500
            minimum: 1
            default: 50
            title: Limit
        - name: status
          in: query
          required: false
          schema:
            anyOf:
              - enum:
                  - queued
                  - running
                  - completed
                  - review_required
                  - failed
                type: string
              - type: 'null'
            title: Status
        - name: source
          in: query
          required: false
          schema:
            anyOf:
              - enum:
                  - upload
                  - email
                type: string
              - type: 'null'
            title: Source
        - name: since
          in: query
          required: false
          schema:
            anyOf:
              - type: string
                format: date-time
              - type: 'null'
            description: Jobs created at or after this time
            title: Since
          description: Jobs created at or after this time
        - name: finished_since
          in: query
          required: false
          schema:
            anyOf:
              - type: string
                format: date-time
              - type: 'null'
            description: >-
              Only jobs that finished (completed, review_required or failed) at
              or after this time, oldest finish first
            title: Finished Since
          description: >-
            Only jobs that finished (completed, review_required or failed) at or
            after this time, oldest finish first
        - name: end_user_id
          in: query
          required: false
          schema:
            anyOf:
              - type: string
                maxLength: 255
              - type: 'null'
            description: As sent to `POST /v1/parse`
            title: End User Id
          description: As sent to `POST /v1/parse`
        - name: external_business_id
          in: query
          required: false
          schema:
            anyOf:
              - type: string
                maxLength: 255
              - type: 'null'
            description: As sent to `POST /v1/parse`
            title: External Business Id
          description: As sent to `POST /v1/parse`
        - name: cursor
          in: query
          required: false
          schema:
            anyOf:
              - type: string
                maxLength: 64
              - type: 'null'
            title: Cursor
      responses:
        '200':
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/JobListOut'
        '401':
          description: Missing, invalid, revoked or expired API key or token
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '403':
          description: The caller lacks the route's permission, or the tenant is suspended
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '413':
          description: Payload too large
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '422':
          description: Invalid request
      security:
        - APIKeyHeader: []
        - HTTPBearer: []
components:
  schemas:
    JobListOut:
      properties:
        jobs:
          items:
            $ref: '#/components/schemas/JobSummaryOut'
          type: array
          title: Jobs
        next_cursor:
          anyOf:
            - type: string
            - type: 'null'
          title: Next Cursor
          description: >-
            Pass as `cursor` (with the same filters) for the next page; null on
            the last page
      additionalProperties: false
      type: object
      required:
        - jobs
        - next_cursor
      title: JobListOut
    ErrorResponse:
      properties:
        error:
          $ref: '#/components/schemas/ErrorBody'
      additionalProperties: false
      type: object
      required:
        - error
      title: ErrorResponse
    JobSummaryOut:
      properties:
        id:
          type: string
          title: Id
        tenant_id:
          type: string
          title: Tenant Id
        status:
          type: string
          enum:
            - queued
            - running
            - completed
            - review_required
            - failed
          title: Status
        source:
          type: string
          enum:
            - upload
            - email
          title: Source
        created_at:
          type: number
          title: Created At
          description: Unix seconds
        updated_at:
          type: number
          title: Updated At
          description: Unix seconds
        completed_at:
          anyOf:
            - type: number
            - type: 'null'
          title: Completed At
          description: Unix seconds; set once the job finished
        error:
          anyOf:
            - type: string
            - type: 'null'
          title: Error
        intake:
          $ref: '#/components/schemas/JobIntakeOut'
      additionalProperties: false
      type: object
      required:
        - id
        - tenant_id
        - status
        - source
        - created_at
        - updated_at
        - intake
      title: JobSummaryOut
    ErrorBody:
      properties:
        code:
          type: string
          title: Code
          description: unauthenticated, forbidden, not_found, conflict, limit_reached, ...
        message:
          type: string
          title: Message
        request_id:
          type: string
          title: Request Id
          description: Also in the X-Request-ID response header
      additionalProperties: false
      type: object
      required:
        - code
        - message
        - request_id
      title: ErrorBody
    JobIntakeOut:
      properties:
        source:
          type: string
          enum:
            - upload
            - email
          title: Source
        received_at:
          type: string
          format: date-time
          title: Received At
          description: When the email was received, or the upload submitted
        broker_email:
          anyOf:
            - type: string
            - type: 'null'
          title: Broker Email
          description: 'The ISO / broker: the form field, or the email''s sender'
        business_name:
          anyOf:
            - type: string
            - type: 'null'
          title: Business Name
        business_ein:
          anyOf:
            - type: string
            - type: 'null'
          title: Business Ein
        external_business_id:
          anyOf:
            - type: string
            - type: 'null'
          title: External Business Id
        end_user_id:
          anyOf:
            - type: string
            - type: 'null'
          title: End User Id
        contact_email:
          anyOf:
            - type: string
            - type: 'null'
          title: Contact Email
        requested_advance_amount:
          anyOf:
            - type: string
            - type: 'null'
          title: Requested Advance Amount
        email:
          anyOf:
            - $ref: '#/components/schemas/JobEmailOut'
            - type: 'null'
          description: Null for an upload
        files:
          items:
            $ref: '#/components/schemas/JobFileOut'
          type: array
          title: Files
          description: '`GET /v1/jobs/{job_id}/files/{position}` returns each one''s bytes'
      additionalProperties: false
      type: object
      required:
        - source
        - received_at
      title: JobIntakeOut
      description: >-
        How a job's packet came in and what it declared: set from submission,
        whatever the job's status.
    JobEmailOut:
      properties:
        email_id:
          anyOf:
            - type: string
            - type: 'null'
          title: Email Id
          description: >-
            The tracked email that started the job: `GET /v1/emails/{email_id}`
            (absent on jobs from before email tracking)
        provider_message_id:
          anyOf:
            - type: string
            - type: 'null'
          title: Provider Message Id
          description: Its Message-ID (or the provider's id)
        mailbox_id:
          anyOf:
            - type: string
            - type: 'null'
          title: Mailbox Id
          description: >-
            The mailbox it arrived through; null when posted to
            `/v1/intake/email` with an API key
        from:
          anyOf:
            - type: string
            - type: 'null'
          title: From
          description: Its From address
        to:
          anyOf:
            - items:
                type: string
              type: array
            - type: 'null'
          title: To
          description: Its To addresses
        subject:
          anyOf:
            - type: string
            - type: 'null'
          title: Subject
        received_at:
          anyOf:
            - type: string
            - type: 'null'
          title: Received At
          description: When it was received, ISO 8601
        attachment_count:
          anyOf:
            - type: integer
            - type: 'null'
          title: Attachment Count
          description: Attachments, processable or not
        processable_attachments:
          anyOf:
            - type: integer
            - type: 'null'
          title: Processable Attachments
          description: PDF, PNG and JPEG documents taken
        thread_id:
          anyOf:
            - type: string
            - type: 'null'
          title: Thread Id
          description: >-
            Its thread (the mailbox and the thread's root message): `GET
            /v1/emails/threads/{thread_id}` lists every email of it
        supersedes:
          anyOf:
            - type: string
            - type: 'null'
          title: Supersedes
          description: The thread's earlier job this one replaces
        routing:
          anyOf:
            - $ref: '#/components/schemas/EmailRoutingOut'
            - type: 'null'
          description: The router's verdict; null when not routed
      additionalProperties: true
      type: object
      title: JobEmailOut
      description: 'The email a job came in (only its envelope: the body is never kept).'
    JobFileOut:
      properties:
        position:
          type: integer
          title: Position
          description: From 0, in upload (or attachment) order
        filename:
          type: string
          title: Filename
        content_type:
          type: string
          title: Content Type
          description: As declared by the uploader or the email
        sha256:
          type: string
          title: Sha256
        bytes:
          type: integer
          title: Bytes
        from_email:
          type: boolean
          title: From Email
          description: An email attachment
      additionalProperties: false
      type: object
      required:
        - position
        - filename
        - content_type
        - sha256
        - bytes
        - from_email
      title: JobFileOut
      description: >-
        One file the job was sent, kept sealed in storage until the job is
        erased.
    EmailRoutingOut:
      properties:
        intent:
          type: string
          enum:
            - new_submission
            - more_documents
            - question_or_update
            - internal_note
            - contract_or_funding
            - not_deal_related
          title: Intent
        confidence:
          type: number
          title: Confidence
          description: >-
            The model's own confidence in that intent, 0-1 (not a measured
            probability)
        model:
          type: string
          title: Model
          description: The router asked (typesafe/jev-router)
        served_model:
          anyOf:
            - type: string
            - type: 'null'
          title: Served Model
          description: The model the router chose
      additionalProperties: false
      type: object
      required:
        - intent
        - confidence
        - model
      title: EmailRoutingOut
      description: >-
        What TypeSafe's Jev Router decided an email is for
        (``EMAIL_ROUTING_API_KEY``).
  securitySchemes:
    APIKeyHeader:
      type: apiKey
      description: API key
      in: header
      name: X-API-Key
    HTTPBearer:
      type: http
      description: API key or AuthKit access token
      scheme: bearer

````

This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.