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

# Start a client project

> Create one deep client report against the shared Agency project quota. Never falls back to personal credits. Async: 202 on a new dispatch, 200 with reused=true when the clientRunId replays an existing report. clientRunId is scoped to the calling key; reusing one that belongs to a different workspace is 409 CLIENT_RUN_ID_CONFLICT. A response carrying dispatchPending=true means the dispatch outcome is unknown - retry the same clientRunId and arguments after 60 seconds; recovery reuses the original report and credit. 503 TRIGGER_DISPATCH_FAILED is a definite rejection: the credit was restored and that request stays failed on replay, so a new attempt needs a new clientRunId. Creating a report here does not email the client or publish a share link.




## OpenAPI

````yaml /api-reference/openapi.yaml post /api/agent/agency/analyses
openapi: 3.1.0
info:
  title: Preuve Agent API
  version: '2.0'
  summary: Startup idea validation for AI agents and scripts.
  description: >
    API for programmatic startup idea validation: a 0-100 viability score,
    competitors, market sizing and source-linked evidence from 60+ live sources.
    Every request carries a single x-preuve-key header holding the whole API key
    - no signing, no bearer token. See the Authentication guide.


    Every response is JSON, errors included: an unknown route answers 404 with
    the same { error, code } envelope as every other failure, so branch on code
    and never parse a message. Error codes are stable and retired ones stay
    listed on the Errors page. scanType "starter" costs nothing and returns a
    real report, which makes it the sandbox: an agent can exercise the whole
    create - poll - enrich - export loop without spending a cent. GET /api/agent
    is the unauthenticated discovery document.
  termsOfService: https://preuve.ai/terms
  contact:
    name: Preuve AI developer support
    url: https://preuve.ai/support
    email: hello@preuve.ai
servers:
  - url: https://preuve.ai
security:
  - preuveKey: []
tags:
  - name: Discovery
    description: Unauthenticated documents that describe the API itself.
  - name: Analyses
    description: One idea at a time - create, poll, enrich, export.
  - name: Batches
    description: Up to five analyses in one idempotent batch.
  - name: Tools
    description: Utilities that spend no scan quota.
  - name: Agency
    description: >-
      Client work on a Consultant or Agency workspace (agency:read /
      agency:write).
externalDocs:
  description: Developer docs - quickstart, authentication, quotas, error codes
  url: https://docs.preuve.ai/
paths:
  /api/agent/agency/analyses:
    post:
      tags:
        - Agency
      summary: Start a client project
      description: >
        Create one deep client report against the shared Agency project quota.
        Never falls back to personal credits. Async: 202 on a new dispatch, 200
        with reused=true when the clientRunId replays an existing report.
        clientRunId is scoped to the calling key; reusing one that belongs to a
        different workspace is 409 CLIENT_RUN_ID_CONFLICT. A response carrying
        dispatchPending=true means the dispatch outcome is unknown - retry the
        same clientRunId and arguments after 60 seconds; recovery reuses the
        original report and credit. 503 TRIGGER_DISPATCH_FAILED is a definite
        rejection: the credit was restored and that request stays failed on
        replay, so a new attempt needs a new clientRunId. Creating a report here
        does not email the client or publish a share link.
      operationId: createAgencyAnalysis
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateAgencyAnalysisRequest'
      responses:
        '200':
          description: Existing report replayed via clientRunId (reused=true).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AgencyReportStatus'
        '202':
          description: Client report created and dispatched.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AgencyReportStatus'
        '400':
          description: >
            Invalid body (CLIENT_RUN_ID_REQUIRED, IDEA_TOO_SHORT,
            INVALID_SCAN_TYPE, INVALID_INPUT, INVALID_JSON).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '402':
          description: >
            The workspace has no payment method on file
            (PAYMENT_METHOD_REQUIRED). Deliberately not a 403: nothing is wrong
            with the key or the caller's permissions, and the fix is a billing
            action the owner takes in the Agency dashboard, after which the same
            clientRunId starts normally.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError'
        '403':
          description: >
            Key does not carry agency:write (INSUFFICIENT_SCOPE), the member
            lacks scan permission in the workspace (AGENCY_PERMISSION_REQUIRED),
            no shared project credits (AGENCY_QUOTA_EXCEEDED), workspace pending
            approval (AGENCY_NOT_APPROVED), account suspended
            (ACCOUNT_SUSPENDED), or blocked address (IP_BLOCKED). Billing setup
            is NOT here - it answers 402, above. agency:write is not implied by
            analysis:write and did not exist before 2026-09-09, so a key created
            earlier always fails this check - see the AgencyInsufficientScope
            response for the recovery and the condition on each of its two
            routes.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError'
        '404':
          $ref: '#/components/responses/AgencyNotFound'
        '409':
          description: >
            clientRunId belongs to another workspace (CLIENT_RUN_ID_CONFLICT) or
            the reservation is still settling (REQUEST_IN_PROGRESS - retry the
            same clientRunId).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError'
        '413':
          description: Body over the size limit (PAYLOAD_TOO_LARGE).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'
        '503':
          description: >
            Dispatch definitively rejected (TRIGGER_DISPATCH_FAILED, credit
            restored) or generation paused (LLM_PAUSED).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError'
components:
  schemas:
    CreateAgencyAnalysisRequest:
      type: object
      required:
        - clientRunId
        - idea
      properties:
        clientRunId:
          type: string
          maxLength: 200
          description: Idempotency key, scoped to the calling API key.
        idea:
          type: string
          minLength: 40
          maxLength: 50000
        targetMarket:
          type: string
          maxLength: 180
        targetCountry:
          type: string
          maxLength: 80
          default: global
        stage:
          type: string
          maxLength: 80
        budget:
          type: string
          maxLength: 80
        language:
          type: string
          maxLength: 12
          default: en
        scanType:
          type: string
          enum:
            - deep
          description: >
            Optional and deep-only. An Agency project always spends one shared
            deep project credit; any other value is 400 INVALID_SCAN_TYPE.
        publish:
          type: boolean
          enum:
            - false
          description: >
            Not accepted here. Client share links are created in the Agency
            dashboard; publish=true is 400 INVALID_INPUT.
    AgencyReportStatus:
      type: object
      properties:
        reportId:
          type: string
        status:
          type: string
          enum:
            - PENDING
            - PROCESSING
            - COMPLETED
            - FAILED
        scanType:
          type: string
          enum:
            - starter
            - deep
        analysisTier:
          type: string
        readyForExport:
          type: boolean
        reportUrl:
          type: string
          format: uri
        createdAt:
          type: string
          format: date-time
        pollAfterSeconds:
          type: integer
          description: Present only while the report is not terminal.
        reused:
          type: boolean
          description: Present when this clientRunId replayed an existing report.
        dispatchPending:
          type: boolean
          description: >
            The dispatch outcome is unknown. Retry the same clientRunId and
            arguments after 60 seconds; recovery reuses the original report and
            credit. Past 23 hours the recovery text directs you to support
            instead of risking a duplicate job.
        recovery:
          type: string
          description: What to do next, present alongside dispatchPending.
    ApiError:
      type: object
      required:
        - error
        - code
      description: >
        Every error, on every route, unknown paths included. Some codes add
        top-level context fields (limit, tokenBalance, resetAt, upsell, ...); a
        404 NOT_FOUND on an unknown route adds details.discovery,
        details.openapi and details.docs so the caller can recover.
      properties:
        error:
          type: string
          description: Human-readable message. Prose; it changes.
        code:
          type: string
          description: Stable machine-readable code. Branch on this.
        details:
          description: >
            Optional context. An object on most codes (limits, recovery links,
            the method and path of a 404); an array of strings where the route
            forwards a validator's messages, as enrich does on
            INVALID_FOUNDER_PROFILE. Read it, never branch on it - `code` is the
            contract.
          oneOf:
            - type: object
              additionalProperties: true
            - type: array
              items:
                type: string
        limit:
          type: integer
          description: >
            The ceiling that was hit, in the unit the code implies: bytes on
            PAYLOAD_TOO_LARGE, runs in flight on CONCURRENT_LIMIT_REACHED,
            requests in the window on a rate limit.
        remaining:
          type: integer
          description: Requests left in the current window.
        resetAt:
          type: string
          format: date-time
          description: >
            When the window that refused this request reopens. Present on
            DAILY_LIMIT_REACHED from run creation (the idea generator's rolling
            ceiling sends only limit); prefer it over the Retry-After header
            when both are present, since it is absolute.
        tokenBalance:
          type: integer
          description: >-
            Deep-scan credits left on the account. Present on
            INSUFFICIENT_TOKENS.
        scansUsed:
          type: integer
        scansRemaining:
          type: integer
        limitType:
          type: string
          description: Which ceiling refused an AppSumo-backed account.
        upgradeHint:
          type: string
          description: What to change to get past this refusal. Prose, not a contract.
        paused:
          type: boolean
          description: >
            Present only on the LLM-pause 503, where the body is exactly {
            error, code, paused }.
        upsell:
          $ref: '#/components/schemas/ExportUpsell'
    ExportUpsell:
      type: object
      description: >
        Purchase context for a locked export or buyable quota refusal. Export
        upsells are free-owner only; a paid non-regional caller may receive a
        scan_pack top-up on INSUFFICIENT_TOKENS. Ignore it if you only need the
        data contract.
      properties:
        offer:
          type: string
          enum:
            - founder_report
            - scan_pack
          description: >
            "founder_report" is the $59 one-time tier. "scan_pack" is a top-up
            for an account that already bought and has no deep credits left.
        price:
          type: string
          description: Display price text, e.g. "$59 one-time".
        guarantee:
          type: string
          description: >
            Guarantee text for offer=founder_report. Omitted for scan_pack
            top-ups; an empty string is never used.
        url:
          type: string
          format: uri
          description: Purchase or report URL, with attribution query parameters.
        locked:
          type: array
          description: >
            Section names the offer covers. Distinct from the top-level locked
            object, which names response FIELDS.
          items:
            type: string
        note:
          type: string
          description: The same facts as one sentence, for a client that relays text.
  responses:
    Unauthorized:
      description: >
        Missing x-preuve-key header (MISSING_API_KEY), or a malformed, unknown
        or revoked key (INVALID_AGENT_KEY - the same envelope for all three, on
        purpose). Repeated failures are throttled per IP and answer 429.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ApiError'
    AgencyNotFound:
      description: >
        No Agency workspace on this account, or workspace membership was removed
        (AGENCY_NOT_FOUND). Membership is resolved live on every call.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ApiError'
    RateLimited:
      description: >
        Rate limited (RATE_LIMITED or DAILY_LIMIT_REACHED). RATE_LIMITED carries
        a Retry-After header with the seconds until the window resets; the
        quota-shaped codes clear on a different clock and put resetAt in the
        body instead.
      headers:
        Retry-After:
          description: Seconds until the per-minute window resets (RATE_LIMITED only).
          schema:
            type: integer
            minimum: 1
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ApiError'
    InternalError:
      description: >
        Unexpected server error (INTERNAL_ERROR, or TRIGGER_DISPATCH_FAILED when
        a deep scan could not be handed to the job runner). Never tell the user
        their quota is safe: on a deep scan the spend can already have happened
        when the failure hit, and an ambiguous dispatch deliberately keeps the
        report and its funding because the job may have been accepted before its
        response was lost. Treat consumption as unknown and retry with backoff.
        On an operation that takes a clientRunId, retry with the SAME one - that
        reconciles the original run, whatever state it reached, instead of
        starting and charging for a second one. On every other operation there
        is nothing to reconcile: the read or export simply failed, so a plain
        backed-off retry is the whole recovery.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ApiError'
  securitySchemes:
    preuveKey:
      type: apiKey
      in: header
      name: x-preuve-key
      description: >
        Your API key (prv_...), sent as-is. It is the only credential the API
        needs - see the Authentication guide.

````

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