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

# Record where the account first came from

> Records once, for the signed-in account, the first-touch attribution that the numi.sh site saved in the browser before sign-up, such as the campaign tag of the link and the domain of the site that linked to numi.sh. The first call records, and every later call changes nothing and answers `recorded` as false. A field that does not follow its rule is stored as null, and the call still succeeds. The site sends this call by itself, so you need it only if you build your own client of the web app.



## OpenAPI

````yaml POST /account/attribution
openapi: 3.1.0
info:
  title: numi API
  version: 0.1.0
  summary: Company data for US SEC filers.
  description: >
    Segments by XBRL axis (business, product or geography), each axis checked
    against the revenue that the filing reports, and fundamentals as filed.


    Every success response has the form `{ "data": ..., "meta": { "request_id":
    "...", "as_of": "..." } }`, where `as_of` is the time of the response. Every
    error response has the form `{ "error": { "code": "...", "message": "..." },
    "meta": { "request_id": "..." } }`, and some errors have more fields beside
    `code` and `message`.


    The `request_id` is also in the `x-request-id` response header. When you
    write to hello@numi.sh, quote it.
servers:
  - url: https://api.numi.sh/v1
security:
  - ApiKey: []
  - BearerKey: []
paths:
  /account/attribution:
    post:
      summary: Record where the account first came from
      description: >-
        Records once, for the signed-in account, the first-touch attribution
        that the numi.sh site saved in the browser before sign-up, such as the
        campaign tag of the link and the domain of the site that linked to
        numi.sh. The first call records, and every later call changes nothing
        and answers `recorded` as false. A field that does not follow its rule
        is stored as null, and the call still succeeds. The site sends this call
        by itself, so you need it only if you build your own client of the web
        app.
      operationId: recordAttribution
      requestBody:
        required: false
        content:
          application/json:
            schema:
              type: object
              properties:
                source:
                  type:
                    - string
                    - 'null'
                  description: >-
                    The campaign source, from the utm_source tag. It is trimmed
                    and lower-cased, and it holds 1 to 64 characters of a-z,
                    0-9, dot, underscore and hyphen. Any other value is stored
                    as null.
                  example: hn
                medium:
                  type:
                    - string
                    - 'null'
                  description: >-
                    The campaign medium, from the utm_medium tag, under the same
                    rule as `source`.
                  example: post
                campaign:
                  type:
                    - string
                    - 'null'
                  description: >-
                    The campaign name, from the utm_campaign tag, under the same
                    rule as `source`.
                  example: launch
                referrer_host:
                  type:
                    - string
                    - 'null'
                  description: >-
                    The host name of the site that linked to numi.sh, with no
                    scheme, path or port. It holds 1 to 253 characters of a-z,
                    0-9, dot and hyphen, with at least one dot. Any other value
                    is stored as null.
                  example: news.ycombinator.com
                landing_path:
                  type:
                    - string
                    - 'null'
                  description: >-
                    The path of the first page opened. It starts with a slash,
                    holds at most 255 characters, and has no query or fragment.
                    Any other value is stored as null.
                  example: /companies/msft
                first_seen:
                  type:
                    - string
                    - 'null'
                  description: >-
                    The day of the first visit, as YYYY-MM-DD. It must be a day
                    on the calendar from 2026-10-01 to tomorrow in UTC. Any
                    other value is stored as null.
                  example: '2026-10-30'
      responses:
        '200':
          description: Whether this call recorded the attribution.
          headers:
            x-request-id:
              $ref: '#/components/headers/RequestId'
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                  - meta
                properties:
                  data:
                    type: object
                    required:
                      - recorded
                    properties:
                      recorded:
                        type: boolean
                        description: >-
                          True when this call recorded the attribution, false
                          when the account had one already.
                    additionalProperties: false
                  meta:
                    $ref: '#/components/schemas/Meta'
                additionalProperties: false
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/SignInRequired'
        '429':
          $ref: '#/components/responses/RateLimited'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
      security:
        - Session: []
components:
  headers:
    RequestId:
      description: >-
        The id of the request. It is also in `meta.request_id` of the body. When
        you write to hello@numi.sh, quote it.
      schema:
        $ref: '#/components/schemas/RequestId'
  schemas:
    Meta:
      type: object
      description: The metadata of one success response.
      required:
        - request_id
        - as_of
      properties:
        request_id:
          $ref: '#/components/schemas/RequestId'
        as_of:
          type: string
          format: date-time
          description: The time of the response, in UTC.
          example: '2026-10-19T12:34:56.789Z'
      additionalProperties: false
    RequestId:
      type: string
      pattern: ^req_[A-Za-z0-9_-]{16}$
      description: The id of one request.
      example: req_Qm3xYb9Kd2LpR7wA
    ErrorEnvelope:
      type: object
      description: The body of every error response.
      required:
        - error
        - meta
      properties:
        error:
          $ref: '#/components/schemas/ErrorBody'
        meta:
          type: object
          description: The metadata of one error response.
          required:
            - request_id
          properties:
            request_id:
              $ref: '#/components/schemas/RequestId'
          additionalProperties: false
      additionalProperties: false
    ErrorBody:
      type: object
      description: >-
        The error. Some errors have more fields beside the code and the message.
        A `rate_limit_exceeded` error has `retry_after` and `scope`, and a
        `sandbox_restricted` error has `sandbox_companies`, the tickers of the
        companies that you can read with a sandbox key, and a `not_found` error
        for a company, a filing or a note can have `reason`, which says why numi
        has nothing to show.
      required:
        - code
        - message
      properties:
        code:
          type: string
          enum:
            - unauthorized
            - invalid_key
            - rate_limit_exceeded
            - bad_request
            - not_found
            - plan_required
            - sandbox_restricted
            - key_limit
            - conflict
            - internal_error
            - service_unavailable
          description: >
            A code for programs to branch on.


            - `unauthorized`: the request has no API key, or an account route
            has no session.

            - `invalid_key`: the key is not valid, or it was revoked.

            - `rate_limit_exceeded`: the account is at the request limit of its
            plan for the minute or the day, or the IP address is at its limit
            for the minute.

            - `bad_request`: a parameter is missing or wrong, or the body is not
            valid JSON or is too large.

            - `not_found`: nothing matches the request.

            - `plan_required`: the plan of the account does not include the
            endpoint, such as on-demand extraction on the sandbox plan.

            - `sandbox_restricted`: the request is for a company outside the
            sandbox, with a sandbox key.

            - `key_limit`: the account has as many active keys as its plan
            allows.

            - `conflict`: a checkout for an account that is on the Builder plan
            already, or the billing portal for an account with no subscription.

            - `internal_error`: the server failed. When you write to
            hello@numi.sh, quote the request id.

            - `service_unavailable`: the server is not configured for the
            feature, such as accounts, billing or bulk files.
        message:
          type: string
          description: >-
            A sentence that says what went wrong and, where it helps, what to
            do.
        reason:
          anyOf:
            - $ref: '#/components/schemas/ReasonName'
            - type: string
          description: >-
            Why nothing was found, on a `not_found` error that has a reason. A
            later server can send a value that `ReasonName` does not list yet,
            so a client keeps an unknown value as text.
      additionalProperties: true
    ReasonName:
      type: string
      enum:
        - not_covered
        - no_extracted_data
        - not_extracted
        - no_filings
        - no_such_filing
        - no_note
        - none_before_as_of
      description: >
        Why a request found nothing. A `not_found` error can carry it as
        `reason`, and an empty history carries it beside `entries`. Each value
        says what numi holds, not what a filing contains. New values can be
        added; none is renamed.


        - `not_covered`: no company that numi holds matches the ref.

        - `no_extracted_data`: filings of the company are held and no such data
        was read from them. The filing may still describe it in its segment
        note.

        - `not_extracted`: the company's filings are in the data but have not
        been read. A pending filing is read in the next daily run.

        - `no_filings`: the company is known and no filing of it is held yet.

        - `no_such_filing`: the company has no extracted filing with that
        `filing_id`.

        - `no_note`: the filing has no segment note on file.

        - `none_before_as_of`: filings of the period type are held for the
        company, and every one was filed after `as_of`.
  responses:
    BadRequest:
      description: >-
        A parameter is missing or wrong, or the body is not valid JSON or is too
        large.
      headers:
        x-request-id:
          $ref: '#/components/headers/RequestId'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorEnvelope'
          example:
            error:
              code: bad_request
              message: 'limit: must be from 1 to 50'
            meta:
              request_id: req_Qm3xYb9Kd2LpR7wA
    SignInRequired:
      description: The request has no valid session of the numi web app.
      headers:
        x-request-id:
          $ref: '#/components/headers/RequestId'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorEnvelope'
          example:
            error:
              code: unauthorized
              message: Sign in to numi to manage your account.
            meta:
              request_id: req_Qm3xYb9Kd2LpR7wA
    RateLimited:
      description: >-
        The account is at the request limit of its plan for the minute or the
        day, or the IP address is at its limit for the minute. Wait the number
        of seconds in the `Retry-After` header. `scope` is `minute` or `day`,
        for the limit that was reached.
      headers:
        x-request-id:
          $ref: '#/components/headers/RequestId'
        Retry-After:
          description: The number of seconds to wait before the next request.
          schema:
            type: integer
            minimum: 1
            example: 12
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorEnvelope'
          example:
            error:
              code: rate_limit_exceeded
              message: >-
                The sandbox plan has a limit of 20 requests a minute. Retry in
                12 seconds.
              retry_after: 12
              scope: minute
            meta:
              request_id: req_Qm3xYb9Kd2LpR7wA
    ServiceUnavailable:
      description: >-
        The server is not configured for the feature, such as accounts, billing
        or bulk files.
      headers:
        x-request-id:
          $ref: '#/components/headers/RequestId'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorEnvelope'
          example:
            error:
              code: service_unavailable
              message: >-
                Accounts are not configured on this server, so you cannot sign
                in here.
            meta:
              request_id: req_Qm3xYb9Kd2LpR7wA
  securitySchemes:
    ApiKey:
      type: apiKey
      in: header
      name: X-API-Key
      description: A numi API key, sent in the `X-API-Key` header.
    BearerKey:
      type: http
      scheme: bearer
      description: 'A numi API key, sent as `Authorization: Bearer <key>`.'
    Session:
      type: http
      scheme: bearer
      description: >-
        A session of the numi web app, sent as `Authorization: Bearer <token>`.
        Only the account routes take it.

````

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