openapi: 3.1.0
info:
  title: SigPen API
  version: '1.0'
  description: |
    SigPen's REST API for sending documents for electronic signature.

    ## Authentication
    Include your API key in every request:
    - `Authorization: Bearer sp_live_your_key_here` (preferred)
    - `X-API-Key: sp_live_your_key_here` (alternative)

    ## Test Mode
    Use keys starting with `sp_test_` for development. Test documents don't count toward your monthly limit.

    ## Rate Limits
    - Developer (Free): 10 req/min, 200 req/day
    - Professional: 30 req/min, 500 req/day
    - Business: 100 req/min, 5,000 req/day
  contact:
    name: SigPen Support
    url: https://www.sigpen.com/contact
    email: support@sigpen.com
  license:
    name: Proprietary

servers:
  - url: https://www.sigpen.com/api/v1
    description: Production

security:
  - BearerAuth: []
  - ApiKeyHeader: []

components:
  securitySchemes:
    BearerAuth:
      type: http
      scheme: bearer
      description: 'API key as Bearer token: `Authorization: Bearer sp_live_...`'
    ApiKeyHeader:
      type: apiKey
      in: header
      name: X-API-Key
      description: 'API key in header: `X-API-Key: sp_live_...`'

  schemas:
    Error:
      type: object
      properties:
        error:
          type: object
          properties:
            type:
              type: string
              enum: [authentication_error, authorization_error, not_found, validation_error, state_conflict, rate_limit_exceeded, quota_exceeded, internal_error]
            message:
              type: string
            code:
              type: string
            doc_url:
              type: string
            details:
              type: object
              additionalProperties:
                type: string

    Pagination:
      type: object
      properties:
        page:
          type: integer
        per_page:
          type: integer
        total:
          type: integer
        total_pages:
          type: integer

    Document:
      type: object
      properties:
        id:
          type: string
        external_id:
          type: string
          nullable: true
          description: Client-provided ID for correlating webhooks with your own records.
        title:
          type: string
        file_name:
          type: string
        status:
          type: string
          # A signer declining voids the document, so a declined document
          # surfaces as 'voided' (with void_reason naming the decline).
          enum: [draft, sent, completed, voided]
        metadata:
          type: object
          additionalProperties:
            type: string
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time

    Signer:
      type: object
      properties:
        id:
          type: string
        email:
          type: string
        name:
          type: string
        status:
          type: string
          enum: [pending, viewed, signed, declined]
        order:
          type: integer
        signed_at:
          type: string
          format: date-time
          nullable: true
        viewed_at:
          type: string
          format: date-time
          nullable: true
          description: First time this signer opened the signing page.

    WebhookEndpoint:
      type: object
      properties:
        id:
          type: string
        url:
          type: string
        events:
          type: array
          items:
            type: string
        active:
          type: boolean
        description:
          type: string
          nullable: true
        created_at:
          type: string
          format: date-time

paths:
  /documents:
    post:
      summary: Upload a document
      description: Upload a base64-encoded PDF for signing. Counts toward monthly document limit.
      tags: [Documents]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [file, file_name]
              properties:
                file:
                  type: string
                  description: Base64-encoded PDF
                file_name:
                  type: string
                title:
                  type: string
                external_id:
                  type: string
                  description: Your own record ID, stored on the document and returned in every webhook.
                metadata:
                  type: object
                  additionalProperties:
                    type: string
      responses:
        '201':
          description: Document created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Document'
        '422':
          description: Validation error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '429':
          description: Rate limited or quota exceeded

    get:
      summary: List documents
      tags: [Documents]
      parameters:
        - name: status
          in: query
          schema:
            type: string
        - name: page
          in: query
          schema:
            type: integer
            default: 1
        - name: per_page
          in: query
          schema:
            type: integer
            default: 20
            maximum: 100
        - name: search
          in: query
          schema:
            type: string
        - name: sort
          in: query
          schema:
            type: string
            enum: [created_at, updated_at]
        - name: order
          in: query
          schema:
            type: string
            enum: [asc, desc]
      responses:
        '200':
          description: Document list
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/Document'
                  pagination:
                    $ref: '#/components/schemas/Pagination'

  /documents/{id}:
    get:
      summary: Get a document
      tags: [Documents]
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Document details
        '404':
          description: Not found

    delete:
      summary: Delete a document (trash)
      description: Moves a draft document to trash (30-day soft delete).
      tags: [Documents]
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        '204':
          description: Deleted
        '409':
          description: Not in draft status

  /documents/{id}/send:
    post:
      summary: Send document for signature
      tags: [Signing]
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [signers]
              properties:
                signers:
                  type: array
                  items:
                    type: object
                    required: [email, name]
                    properties:
                      email:
                        type: string
                      name:
                        type: string
                      order:
                        type: integer
                        description: Signing order. With order set, signing is sequential and each signer is invited when the previous one completes.
                      fields:
                        type: array
                        description: Inline field placement. Places this signer's fields without the visual editor. Coordinates are PDF points from the top-left of the page.
                        items:
                          type: object
                          required: [type, page, x, y, width, height]
                          properties:
                            type:
                              type: string
                              enum: [signature, initials, date, text, checkbox, number]
                            page:
                              type: integer
                              description: 1-based page number.
                            x:
                              type: number
                            y:
                              type: number
                            width:
                              type: number
                            height:
                              type: number
                            required:
                              type: boolean
                              default: true
                cc:
                  type: array
                  items:
                    type: object
                    properties:
                      email:
                        type: string
                      name:
                        type: string
                message:
                  type: string
                expires_in_days:
                  type: integer
                  default: 30
                skip_email:
                  type: boolean
                  default: false
                  description: Create the signing sessions without sending invitation emails, for embedded signing where your app surfaces the sign URL. With sequential signing (order set), later signers are still emailed at their turn.
      responses:
        '200':
          description: Document sent

  /documents/{id}/void:
    post:
      summary: Void a document
      tags: [Documents]
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [reason]
              properties:
                reason:
                  type: string
                  minLength: 10
      responses:
        '200':
          description: Document voided
        '409':
          description: Cannot void in current state
        '422':
          description: Reason too short

  /documents/{id}/download:
    get:
      summary: Download signed PDF
      tags: [Documents]
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: PDF file
          content:
            application/pdf: {}
        '409':
          description: Document not completed

  /documents/{id}/remind:
    post:
      summary: Send reminder to pending signers
      tags: [Signing]
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                signer_id:
                  type: string
                message:
                  type: string
      responses:
        '200':
          description: Reminders sent

  /documents/{id}/embedded-sign-url:
    post:
      summary: Generate embedded signing URL
      tags: [Embedded Signing]
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [signer_id]
              properties:
                signer_id:
                  type: string
      responses:
        '200':
          description: Signing URL generated
          content:
            application/json:
              schema:
                type: object
                properties:
                  sign_url:
                    type: string
                  expires_at:
                    type: string
                    format: date-time

  /documents/{id}/bulk-send:
    post:
      summary: Bulk send document
      tags: [Bulk Send]
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [recipients]
              properties:
                recipients:
                  type: array
                  items:
                    type: object
                    required: [email, name]
                    properties:
                      email:
                        type: string
                      name:
                        type: string
                subject:
                  type: string
                  description: Stored with each copy and shown in the dashboard. Invitation email subject stays the SigPen standard.
                skip_email:
                  type: boolean
                  default: false
                  description: Create the signing sessions without sending invitation emails, so you can distribute the signing URLs yourself.
      responses:
        '202':
          description: Bulk send started

  /documents/from-template:
    post:
      summary: Create document from template
      tags: [Templates]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [template_id]
              properties:
                template_id:
                  type: string
                role_assignments:
                  type: object
                  description: Keyed by the template role name. Each value is an object with the signer email. The signer's name is supplied later at send time.
                  additionalProperties:
                    type: object
                    properties:
                      email:
                        type: string
                prefill:
                  type: object
                  description: Keyed by the field label (not field ID). Flat string values.
                  additionalProperties:
                    type: string
                title:
                  type: string
                external_id:
                  type: string
                  description: Your own record ID, stored on the document and returned in every webhook.
                metadata:
                  type: object
                  additionalProperties:
                    type: string
      responses:
        '201':
          description: Document created from template

  /documents/from-form:
    post:
      summary: Create document from form (Business only)
      tags: [Forms Library]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [form_id]
              properties:
                form_id:
                  type: string
                prefill:
                  type: object
                title:
                  type: string
      responses:
        '201':
          description: Document created from form

  /account:
    get:
      summary: Get account info and usage
      tags: [Account]
      responses:
        '200':
          description: Account info

  /webhooks:
    get:
      summary: List webhook endpoints
      tags: [Webhooks]
      responses:
        '200':
          description: Webhook list

    post:
      summary: Register a webhook endpoint
      tags: [Webhooks]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [url, events]
              properties:
                url:
                  type: string
                  format: uri
                events:
                  type: array
                  items:
                    type: string
                    enum: [document.sent, document.viewed, document.signed, document.completed, document.declined, document.voided, document.expired]
                description:
                  type: string
      responses:
        '201':
          description: Webhook created (secret shown only once)

  /webhooks/{id}:
    get:
      summary: Get webhook details
      tags: [Webhooks]
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Webhook details

    patch:
      summary: Update webhook endpoint
      tags: [Webhooks]
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Webhook updated

    delete:
      summary: Delete webhook endpoint
      tags: [Webhooks]
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        '204':
          description: Webhook deleted

  /webhooks/{id}/test:
    post:
      summary: Send test webhook event
      tags: [Webhooks]
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Test result

  /templates:
    get:
      summary: List templates
      tags: [Templates]
      responses:
        '200':
          description: Template list

    post:
      summary: Create template from document
      tags: [Templates]
      responses:
        '201':
          description: Template created

  /templates/{id}:
    get:
      summary: Get template details
      tags: [Templates]
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Template details with fields

    patch:
      summary: Update template
      tags: [Templates]
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Template updated

    delete:
      summary: Delete template
      tags: [Templates]
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        '204':
          description: Template deleted

  /forms:
    get:
      summary: List available forms (Business only)
      tags: [Forms Library]
      parameters:
        - name: state
          in: query
          schema:
            type: string
        - name: category
          in: query
          schema:
            type: string
        - name: search
          in: query
          schema:
            type: string
      responses:
        '200':
          description: Form list
        '403':
          description: Business tier required

  /forms/{id}:
    get:
      summary: Get form details (Business only)
      tags: [Forms Library]
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Form details
        '403':
          description: Business tier required

  /bulk-sends/{id}:
    get:
      summary: Check bulk send status
      tags: [Bulk Send]
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Bulk send status
