> ## Documentation Index
> Fetch the complete documentation index at: https://paperplane-justin-winter-s-projects.vercel.app/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Propose a header→field mapping for a bulk import

> Parsing happens in the browser, so only the header row and up to three sample rows ever leave the device — never the full recipient list. Advisory: the user confirms or overrides every field before a row becomes an order.



## OpenAPI

````yaml /openapi.json post /v1/csv-map
openapi: 3.1.0
info:
  title: paperplane API
  version: 1.0.0
  summary: Send real physical mail from code.
  description: >-
    Upload a PDF or paste text; we print it and hand it to USPS the next
    business day. No account required — pay per letter with a Stripe link or a
    prepaid credit code, and use `sandbox: true` to exercise the whole flow for
    free.


    Every failure returns the same envelope: `status`, a stable machine-readable
    `code`, a human `reason`, and a `next` array of concrete recovery steps.


    This document is generated from the same zod schemas the server validates
    with, so it cannot describe an endpoint that does not exist.
  contact:
    name: paperplane support
    url: https://sendpaperplane.com/contact
  license:
    name: Proprietary
    identifier: LicenseRef-Proprietary
  termsOfService: https://sendpaperplane.com/terms
servers:
  - url: https://sendpaperplane.com
    description: Production
security: []
tags:
  - name: Mail
    description: Quote, send, track, and cancel letters.
  - name: Credits
    description: Prepaid balances that pay for letters without a card.
  - name: Reviews
    description: Post-delivery feedback. Identity is derived from the order, not supplied.
  - name: Addresses
    description: >-
      Typeahead and reverse geocoding. Convenience only; USPS verification is
      authoritative.
  - name: Meta
    description: Machine-readable description of this API.
paths:
  /v1/csv-map:
    post:
      tags:
        - Mail
      summary: Propose a header→field mapping for a bulk import
      description: >-
        Parsing happens in the browser, so only the header row and up to three
        sample rows ever leave the device — never the full recipient list.
        Advisory: the user confirms or overrides every field before a row
        becomes an order.
      operationId: mapCsvHeaders
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CsvMapRequest'
            example:
              headers:
                - Full Name
                - Street
                - Unit
                - City
                - State
                - Zip Code
              samples:
                - - Jordan Rivera
                  - 742 Evergreen Terrace
                  - Apt 4
                  - Springfield
                  - IL
                  - '62704'
      responses:
        '200':
          description: Proposed mapping plus its source
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CsvMapResponse'
              example:
                status: ok
                mapping:
                  name:
                    - Full Name
                  line1:
                    - Street
                  line2:
                    - Unit
                  city:
                    - City
                  state:
                    - State
                  zip:
                    - Zip Code
                source: heuristic
        '400':
          description: 'Malformed body — send `{ headers: string[], samples?: string[][] }`.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '429':
          description: Rate limited (10/minute per IP).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
          headers:
            Retry-After:
              description: Seconds to wait before retrying.
              schema:
                type: integer
                example: 60
components:
  schemas:
    CsvMapRequest:
      type: object
      properties:
        headers:
          minItems: 1
          maxItems: 100
          type: array
          items:
            type: string
            maxLength: 200
        samples:
          maxItems: 3
          type: array
          items:
            maxItems: 100
            type: array
            items:
              type: string
              maxLength: 500
      required:
        - headers
    CsvMapResponse:
      type: object
      properties:
        status:
          type: string
          const: ok
        mapping:
          type: object
          propertyNames:
            type: string
            enum:
              - name
              - line1
              - line2
              - city
              - state
              - zip
          additionalProperties:
            type: array
            items:
              type: string
        source:
          type: string
          enum:
            - heuristic
            - llm
      required:
        - status
        - mapping
        - source
    Error:
      type: object
      properties:
        status:
          type: string
          enum:
            - failed
            - action_required
        code:
          type: string
        reason:
          type: string
        next:
          type: array
          items:
            type: string
      required:
        - status
        - code
        - reason
        - next

````