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

# Insert rows

> Insert rows into an existing table. Returns a batch ID immediately — poll
`GET /batches/{batchId}` for enrichment progress and results.

**Column matching:** Field names in each row object are matched to **input** columns
by **slug** (exact match). Only columns with `kind: "input"` are accepted —
enrichment and score columns are populated automatically. Use `GET /tables` to
discover available column slugs and their kinds.

**Enrichment:** By default (`enrich: true`), all auto-trigger enrichment columns run
immediately after insertion. Set `enrich: false` to insert data without triggering enrichment.

**Deduplication & exclusion lists** apply automatically — the same table-level rules
from the UI are enforced. Duplicate or excluded rows are inserted but may not enrich.

**Limits:**
- Maximum **100 rows** per request.
- Free-plan tables are capped at **30 rows** total.




## OpenAPI

````yaml /openapi-v1.yaml post /tables/{tableId}/rows
openapi: 3.1.0
info:
  title: Origami API
  version: '1.0'
  description: >
    > **Deprecated.** v1 is superseded by the canonical

    > [v2 API](./openapi-v2) (four segments: Agents, Workspace, Sequences,

    > Account). v1 remains fully functional with **no removal date in this

    > release** — but new integrations should target v2. See the

    > [v1 → v2 migration guide](./api-v1-to-v2-migration): `GET /tables` →

    > `GET /api/v2/tables`, `GET /tables/:id/rows` →

    > `GET /api/v2/tables/:tableId/rows`, `POST /tables/:id/rows` →

    > `POST /api/v2/tables/:tableId/rows/upsert` (upsert; no bare insert),

    > `GET /batches/:id` → `GET /api/v2/batches/:batchId`, `GET /credits` →

    > `GET /api/v2/account/credits`.


    The Origami API lets you enrich company and people data programmatically.

    See the [Quickstart](/quickstart) for a walkthrough with curl examples.


    All write endpoints are **asynchronous** — a `POST` returns a `batchId`
    immediately.

    Poll `GET /batches/{batchId}` for progress and enriched results.


    ## Rate limits


    Limits apply in layers: **per IP** (all requests), **per organization**
    (after a valid API key), and an extra **insert** cap on row writes.


    | Scope | Limit |

    |-------|-------|

    | Per client IP | **300 requests / minute** |

    | Per organization (all endpoints) | **100 requests / minute** |

    | `POST /tables/{tableId}/rows` (per organization) | **10 requests /
    minute** |


    Each layer exposes its own response headers:
    `X-RateLimit-Limit-{IP|Global|Insert}`,

    `X-RateLimit-Remaining-{IP|Global|Insert}`, and
    `X-RateLimit-Reset-{IP|Global|Insert}` (Unix seconds).

    A request may include several of these when multiple layers apply.


    ## Request IDs


    Every response includes an `X-Request-Id` header. You can pass your own ID

    via the `X-Request-Id` request header (1–64 alphanumeric / dash / underscore

    characters); otherwise the server generates a UUID. Include this ID in
    support

    requests to help us trace issues.


    ## Errors


    All errors follow a consistent format:


    ```json

    {
      "error": "Human-readable error message",
      "code": "MACHINE_READABLE_CODE"
    }

    ```


    Some errors include additional context fields like `path`, `fields`, `hint`,
    `current`, `limit`, or `requested`.


    | Code | Status | Meaning |

    |------|--------|---------|

    | `UNAUTHORIZED` | 401 | Missing, invalid, revoked, or expired API key. |

    | `VALIDATION_ERROR` | 400 | Invalid request parameter or body. |

    | `NOT_FOUND` | 404 | Table or batch not found. |

    | `RATE_LIMITED` | 429 | Too many requests — see rate-limit headers. |

    | `INSUFFICIENT_CREDITS` | 402 | Not enough credits to complete the
    operation. |

    | `SUBSCRIPTION_REQUIRED` | 402 | Feature requires a plan upgrade. |

    | `INTERNAL_ERROR` | 500 | Unexpected server error. |


    ## Webhooks


    Receive sequencer events (sends, replies, connection acceptances) via

    HTTPS POST. Configure endpoints in **Settings → Developers → Webhooks**

    or read the [webhooks reference](/webhooks/overview). The full event

    catalog ships as [openapi-webhooks.yaml](/openapi-webhooks.yaml) so

    coding agents can author working receivers from the spec.
  contact:
    name: Origami Support
    url: https://origami.chat
  license:
    name: Proprietary
servers:
  - url: https://origami.chat/api/v1
    description: Production
security:
  - apiKey: []
tags:
  - name: Tables
    description: List tables and read row data.
  - name: Rows
    description: Insert rows and trigger enrichment.
  - name: Batches
    description: Poll async batch progress and retrieve results.
  - name: Credits
    description: Check your organization's credit balance.
paths:
  /tables/{tableId}/rows:
    post:
      tags:
        - Rows
      summary: Insert rows
      description: >
        Insert rows into an existing table. Returns a batch ID immediately —
        poll

        `GET /batches/{batchId}` for enrichment progress and results.


        **Column matching:** Field names in each row object are matched to
        **input** columns

        by **slug** (exact match). Only columns with `kind: "input"` are
        accepted —

        enrichment and score columns are populated automatically. Use `GET
        /tables` to

        discover available column slugs and their kinds.


        **Enrichment:** By default (`enrich: true`), all auto-trigger enrichment
        columns run

        immediately after insertion. Set `enrich: false` to insert data without
        triggering enrichment.


        **Deduplication & exclusion lists** apply automatically — the same
        table-level rules

        from the UI are enforced. Duplicate or excluded rows are inserted but
        may not enrich.


        **Limits:**

        - Maximum **100 rows** per request.

        - Free-plan tables are capped at **30 rows** total.
      operationId: insertRows
      parameters:
        - $ref: '#/components/parameters/tableId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/InsertRowsRequest'
            examples:
              basic:
                summary: Insert two companies
                value:
                  rows:
                    - company-name: Acme Corp
                      website: acme.com
                    - company-name: Beta Inc
                      website: beta.io
              withoutEnrichment:
                summary: Insert without triggering enrichment
                value:
                  rows:
                    - company-name: Gamma LLC
                      website: gamma.dev
                  enrich: false
              idempotentRetry:
                summary: Idempotent insert with caller-generated batch ID
                value:
                  rows:
                    - company-name: Acme Corp
                      website: acme.com
                  batchId: 550e8400-e29b-41d4-a716-446655440000
      responses:
        '200':
          description: >
            Idempotent replay — a batch with the provided `batchId` already
            exists.

            The original batch ID is returned without inserting new rows.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BatchCreated'
              example:
                batchId: 550e8400-e29b-41d4-a716-446655440000
        '201':
          description: Rows accepted for processing.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BatchCreated'
              example:
                batchId: f47ac10b-58cc-4372-a567-0e02b2c3d479
        '400':
          description: Validation error.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
              examples:
                nonInputColumns:
                  summary: Request contains enrichment or score column slugs
                  value:
                    error: Only input columns can be set via the API
                    code: NON_INPUT_COLUMNS
                    fields:
                      - ceo-email
                    hint: Enrichment and score columns are populated automatically
                unknownFields:
                  summary: Request contains fields that don't match any column slug
                  value:
                    error: Unknown fields
                    code: UNKNOWN_FIELDS
                    fields:
                      - Foo
                      - Bar
                    hint: Use input column slugs from GET /tables
                rowLimit:
                  summary: Table has reached its plan's row limit
                  value:
                    error: Row limit exceeded
                    code: ROW_LIMIT_EXCEEDED
                    current: 28
                    limit: 30
                    requested: 5
                validationError:
                  summary: >-
                    Invalid request body (e.g. empty rows, too many rows,
                    invalid batchId)
                  value:
                    error: rows must be a non-empty array
                    code: VALIDATION_ERROR
                    path: rows
        '401':
          $ref: '#/components/responses/Unauthorized'
        '402':
          $ref: '#/components/responses/PaymentRequired'
        '404':
          $ref: '#/components/responses/TableNotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
        '500':
          $ref: '#/components/responses/InternalError'
components:
  parameters:
    tableId:
      name: tableId
      in: path
      required: true
      schema:
        type: string
        format: uuid
      description: The table UUID. Use `GET /tables` to list available tables.
  schemas:
    InsertRowsRequest:
      type: object
      required:
        - rows
      properties:
        rows:
          type: array
          minItems: 1
          maxItems: 100
          items:
            type: object
            additionalProperties: true
            description: >
              Key-value pairs where keys are **input** column slugs (from `GET
              /tables`,

              where `kind` is `"input"`) and values are the cell data to insert.
          description: |
            Array of row objects to insert. Each row is a flat object mapping
            input column slugs to values. Maximum 100 rows per request.
        enrich:
          type: boolean
          default: true
          description: |
            Whether to trigger enrichment after inserting rows.
            Defaults to `true`. Set to `false` to insert data without
            running enrichment — the batch will immediately be `"complete"`.
        batchId:
          type: string
          format: uuid
          description: |
            Optional caller-generated UUID for idempotent retries. If a batch
            with this ID already exists for the organization, the server returns
            the existing batch (HTTP 200) without inserting rows again. Generate
            a UUID client-side and include it with every attempt to make retries
            safe after network failures.
    BatchCreated:
      type: object
      required:
        - batchId
      properties:
        batchId:
          type: string
          format: uuid
          description: >
            Unique identifier for the batch. Use this to poll `GET
            /batches/{batchId}`

            for progress and results.
    Error:
      type: object
      required:
        - error
        - code
      properties:
        error:
          type: string
          description: Human-readable error message.
        code:
          type: string
          description: Machine-readable error code.
      additionalProperties: true
  responses:
    Unauthorized:
      description: Missing, invalid, revoked, or expired API key.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            error: Invalid API key
            code: UNAUTHORIZED
    PaymentRequired:
      description: Insufficient credits or subscription upgrade needed.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          examples:
            insufficientCredits:
              summary: Not enough credits
              value:
                error: Insufficient credits
                code: INSUFFICIENT_CREDITS
                creditsRequired: 50
                creditsAvailable: 12
            subscriptionRequired:
              summary: Plan upgrade needed
              value:
                error: Subscription required
                code: SUBSCRIPTION_REQUIRED
                hint: Upgrade to a Growth or Business plan to use this feature.
    TableNotFound:
      description: Table not found or does not belong to this organization.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            error: Table not found
            code: NOT_FOUND
    RateLimited:
      description: Rate limit exceeded.
      headers:
        X-RateLimit-Limit-IP:
          schema:
            type: integer
          description: IP bucket — maximum requests allowed in the window.
        X-RateLimit-Remaining-IP:
          schema:
            type: integer
          description: IP bucket — requests remaining in the current window.
        X-RateLimit-Reset-IP:
          schema:
            type: integer
          description: IP bucket — Unix timestamp when the window resets.
        X-RateLimit-Limit-Global:
          schema:
            type: integer
          description: Organization bucket — maximum requests allowed in the window.
        X-RateLimit-Remaining-Global:
          schema:
            type: integer
          description: Organization bucket — requests remaining in the current window.
        X-RateLimit-Reset-Global:
          schema:
            type: integer
          description: Organization bucket — Unix timestamp when the window resets.
        X-RateLimit-Limit-Insert:
          schema:
            type: integer
          description: >-
            Insert bucket — maximum requests allowed in the window (write routes
            only).
        X-RateLimit-Remaining-Insert:
          schema:
            type: integer
          description: Insert bucket — requests remaining in the current window.
        X-RateLimit-Reset-Insert:
          schema:
            type: integer
          description: Insert bucket — Unix timestamp when the window resets.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            error: Rate limit exceeded
            code: RATE_LIMITED
    InternalError:
      description: Unexpected server error.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            error: Internal server error
            code: INTERNAL_ERROR
  securitySchemes:
    apiKey:
      type: http
      scheme: bearer
      bearerFormat: og_live_*
      description: |
        API key with `og_live_` prefix. Create keys in **Settings → API Keys**.

        Pass in the `Authorization` header:
        ```
        Authorization: Bearer og_live_abc123...
        ```

````