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

# Upload an invoice PDF and publish the invoice

> Uploads a PDF for an existing draft invoice and publishes it: the file becomes the invoice's single invoicePDFs entry and the status is set to "pending". Preconditions (each answered with 400): the API key's facility must own the invoice, the associated work order must be in Completed display status, and supplier invoice editing must be allowed. Multipart upload, max 512 MB.



## OpenAPI

````yaml /openapi/supplier.json post /v1/supplier/invoice/file/upload_and_publish/{invoiceId}
openapi: 3.1.0
info:
  title: OpenWrench Supplier API
  version: 1.0.0
  description: >-
    External REST API for OpenWrench suppliers (service providers).


    ## Authentication

    Every request must carry a supplier API key in the `X-API-KEY` header. The
    key resolves to a supplier contact; all reads and writes are tenant-scoped
    to that contact's supplier facility/company. Missing or invalid keys are
    answered with 401.


    ## Rate limiting

    10 requests per 20-second window per API key. Requests beyond that are
    answered with 429 and the standard error envelope. (A few endpoints, noted
    in their descriptions, are not rate-limited.)


    ## Response envelope

    Single entity: `{"type": "<EntityName>", "data": {...}, "status": "ok"}`.
    Lists: `{"type": "<EntityName>", "data": [...], "count": <total>, "status":
    "ok"}`. count_by endpoints return the integer count in `data`. The `type`
    label is hard-coded on some endpoints (e.g. "WorkOrderNotes", "pingpong")
    and derived via Scala reflection on generic CRUD endpoints, where it may
    appear as the fully qualified server class name - treat it as informational.
    Errors: `{"message": "...", "type": "<ExceptionType>", "status": "error",
    "traceId": "..."}` with HTTP 400 (bad input, and also permission-denied
    reads/writes), 401 (missing/invalid key), 404 (not found), 429 (rate limit).


    ## Pagination

    List endpoints accept `offset`, `limit` (default 10, max 25 - the supplier
    locations list caps at 10), `sort_by` and `order` (`asc`|`desc`), plus
    entity-specific query-string filters on indexed columns.


    ## Data masking

    For third-party suppliers (keys whose supplier company is not a buyer's
    internal service team), work order, location and invoice responses are
    masked: buyer-private fields are blanked before the response is returned.


    ## Date formats

    Date-time fields serialize as strings in one of two shapes depending on the
    underlying type: ISO 8601 with offset (e.g. `2026-08-14T13:05:22.000-07:00`)
    for most timestamps, or `yyyy-MM-dd HH:mm:ss.S` (space-separated, no offset)
    for database timestamp fields. Plain dates are `yyyy-MM-dd`. When sending
    date-times, ISO 8601 is accepted.


    ## Required headers

    Every request must carry BOTH `X-API-KEY` (your API key) and `OW-KEY` (the
    OpenWrench shared secret issued with it). Requests missing either return
    401.
servers:
  - url: https://api.useopenwrench.com/api/external
security:
  - ApiKeyAuth: []
    OwKeyAuth: []
tags:
  - name: Ping
  - name: Buyer Companies
  - name: Locations
  - name: Regions
  - name: Asset Types
  - name: Assets
  - name: Asset Labels
    description: Read the asset label catalog and replace the labels applied to an asset.
  - name: Work Orders
  - name: Work Order Labels
    description: >-
      Read the work order label catalog visible to your key and replace the
      labels applied to a work order.
  - name: Service Calls
  - name: WrenchMode
    description: Read-only WrenchMode work logs and technician time analytics
  - name: Proposals
  - name: Invoices
  - name: Files
  - name: Purchase Requests
  - name: Purchase Orders
  - name: Purchase Order Receipts
  - name: Parts
  - name: Equipment Types
  - name: Stock Location Inventory
  - name: Vendors
  - name: Users
paths:
  /v1/supplier/invoice/file/upload_and_publish/{invoiceId}:
    post:
      tags:
        - Invoices
      summary: Upload an invoice PDF and publish the invoice
      description: >-
        Uploads a PDF for an existing draft invoice and publishes it: the file
        becomes the invoice's single invoicePDFs entry and the status is set to
        "pending". Preconditions (each answered with 400): the API key's
        facility must own the invoice, the associated work order must be in
        Completed display status, and supplier invoice editing must be allowed.
        Multipart upload, max 512 MB.
      operationId: uploadInvoicePdfAndPublish
      parameters:
        - name: invoiceId
          in: path
          required: true
          schema:
            type: integer
            format: int64
          description: Invoice id
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              type: object
              properties:
                file:
                  type: string
                  format: binary
                  description: The invoice PDF
              required:
                - file
      responses:
        '200':
          description: Successful response
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/InvoiceResponse'
        '400':
          description: >-
            Bad input, validation failure, or the API key's contact could not be
            resolved
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          description: Missing or invalid X-API-KEY
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '429':
          description: Rate limit exceeded (10 requests per 20-second window)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
components:
  schemas:
    InvoiceResponse:
      type: object
      properties:
        type:
          type: string
          description: >-
            Entity type label, e.g. "Invoice". Endpoints that derive it via
            Scala reflection (most generic list/get endpoints) may return the
            fully qualified server class name instead (e.g.
            "models.workorder.WorkOrder"); treat this field as informational.
        data:
          $ref: '#/components/schemas/Invoice'
        status:
          type: string
          description: '"ok" on success'
          enum:
            - ok
      required:
        - type
        - data
        - status
    Error:
      type: object
      properties:
        message:
          type: string
          description: Human-readable error message
        type:
          type: string
          description: >-
            Exception type identifier. Known values (exact casing):
            "unauthorizedException", "authorizationException", "ParseException",
            "UnexpectedException", "NotFoundException", "BadRequestException",
            "PaymentRequiredException", "InvalidInputDataException",
            "ConflictException", "ForbiddenException", "InvalidInputException"
        status:
          type: string
          enum:
            - error
        traceId:
          type: string
          description: >-
            Server-generated trace identifier for support (11 alphanumeric
            characters)
      required:
        - message
        - type
        - status
        - traceId
      description: Standard error envelope returned for 4xx/5xx responses.
    Invoice:
      type: object
      properties:
        id:
          anyOf:
            - type: integer
              format: int64
            - type: 'null'
        status:
          type: string
          description: >-
            draft | pending | approved | disputed | paid | transferred | pastdue
            | void | processing (persisted lowercase)
        scope:
          anyOf:
            - type: string
            - type: 'null'
        externalWorkOrderId:
          anyOf:
            - type: string
            - type: 'null'
        paymentTerms:
          anyOf:
            - type: string
            - type: 'null'
        poNumber:
          anyOf:
            - type: string
            - type: 'null'
        glCode:
          anyOf:
            - type: string
            - type: 'null'
        dueDate:
          anyOf:
            - type: string
              format: date-time
              description: ISO 8601 date-time
            - type: 'null'
        publishedAt:
          anyOf:
            - type: string
              format: date-time
              description: ISO 8601 date-time
            - type: 'null'
        approvedAt:
          anyOf:
            - type: string
              format: date-time
              description: ISO 8601 date-time
            - type: 'null'
        processedAt:
          anyOf:
            - type: string
              format: date-time
              description: ISO 8601 date-time
            - type: 'null'
        markedPaidAt:
          anyOf:
            - type: string
              format: date-time
              description: ISO 8601 date-time
            - type: 'null'
        dateOfInvoice:
          anyOf:
            - type: string
              format: date-time
              description: ISO 8601 date-time
            - type: 'null'
        invoiceNumber:
          anyOf:
            - type: string
            - type: 'null'
        invoiceNotes:
          type: array
          items:
            $ref: '#/components/schemas/Note'
        invoiceTaxRate:
          anyOf:
            - type: string
            - type: 'null'
        invoiceTax:
          anyOf:
            - type: string
            - type: 'null'
        invoiceTotalBeforeTax:
          anyOf:
            - type: string
            - type: 'null'
        invoiceTotalAfterTax:
          anyOf:
            - type: string
            - type: 'null'
        laborTotalBeforeTax:
          anyOf:
            - type: string
            - type: 'null'
        materialTotalBeforeTax:
          anyOf:
            - type: string
            - type: 'null'
        travelTotalBeforeTax:
          anyOf:
            - type: string
            - type: 'null'
        freightTotalBeforeTax:
          anyOf:
            - type: string
            - type: 'null'
        miscTotalBeforeTax:
          anyOf:
            - type: string
            - type: 'null'
        taxLineItems:
          anyOf:
            - type: array
              items:
                type: object
                properties:
                  taxType:
                    type: string
                  amount:
                    type: string
                required:
                  - taxType
                  - amount
            - type: 'null'
        attachments:
          type: array
          items:
            $ref: '#/components/schemas/FileDetails'
        invoicePDFs:
          type: array
          items:
            type: object
            properties:
              orderingId:
                type: integer
              link:
                type: string
            required:
              - orderingId
              - link
        workOrderId:
          type: integer
          format: int64
        serviceCallIds:
          type: array
          items:
            type: integer
            format: int64
        problemTypeId:
          anyOf:
            - type: integer
            - type: 'null'
        supplierFacilityId:
          type: integer
        buyerSupplierRelationship:
          anyOf:
            - type: object
              properties: {}
              description: External-API projection of the buyer-supplier relationship
              additionalProperties: true
            - type: 'null'
        locationId:
          type: integer
          format: int64
        buyerFacilityId:
          type: integer
        buyerCompanyId:
          type: integer
        isBuyerUploaded:
          anyOf:
            - type: boolean
            - type: 'null'
        createdBy:
          anyOf:
            - type: string
            - type: 'null'
        createdAt:
          anyOf:
            - type: string
              format: date-time
              description: ISO 8601 date-time
            - type: 'null'
        updatedAt:
          anyOf:
            - type: string
              format: date-time
              description: ISO 8601 date-time
            - type: 'null'
        isDeleted:
          type: boolean
        deletedAt:
          anyOf:
            - type: string
              format: date-time
              description: ISO 8601 date-time
            - type: 'null'
        laborLineItems:
          type: array
          items:
            type: object
            properties:
              description:
                anyOf:
                  - type: string
                  - type: 'null'
              roleType:
                anyOf:
                  - type: string
                  - type: 'null'
              laborType:
                anyOf:
                  - type: string
                  - type: 'null'
              numberOfTech:
                type: integer
              hourlyRate:
                anyOf:
                  - type: string
                  - type: 'null'
              seconds:
                type: integer
                format: int64
              amount:
                anyOf:
                  - type: string
                  - type: 'null'
            required:
              - numberOfTech
              - seconds
        materialLineItems:
          type: array
          items:
            type: object
            properties:
              description:
                anyOf:
                  - type: string
                  - type: 'null'
              partNumber:
                anyOf:
                  - type: string
                  - type: 'null'
              partId:
                anyOf:
                  - type: integer
                    format: int64
                  - type: 'null'
              equipmentNumber:
                anyOf:
                  - type: string
                  - type: 'null'
              equipmentId:
                anyOf:
                  - type: integer
                    format: int64
                  - type: 'null'
              unitType:
                type: string
              unitPrice:
                anyOf:
                  - type: string
                  - type: 'null'
              quantity:
                type: string
              amount:
                anyOf:
                  - type: string
                  - type: 'null'
            required:
              - unitType
              - quantity
        travelLineItems:
          type: array
          items:
            $ref: '#/components/schemas/SimpleLineItem'
        freightLineItems:
          type: array
          items:
            $ref: '#/components/schemas/SimpleLineItem'
        miscLineItems:
          type: array
          items:
            $ref: '#/components/schemas/SimpleLineItem'
      description: >-
        Invoice in its external-API projection (InvoiceExternalAPI = full
        Invoice fields with buyerSupplierRelationship replaced by an external
        projection). Abridged - the full model carries approval-hierarchy,
        dispute and hydrated relation fields. For third-party suppliers
        responses are masked.
      additionalProperties: true
    Note:
      type: object
      properties:
        id:
          anyOf:
            - type: integer
              format: int64
              description: >-
                Per-note row id, populated on read where the note store is
                normalized
            - type: 'null'
        text:
          type: string
        photo:
          anyOf:
            - type: string
            - type: 'null'
        video:
          anyOf:
            - type: string
            - type: 'null'
        audio:
          anyOf:
            - type: string
            - type: 'null'
        otherFile:
          anyOf:
            - type: string
            - type: 'null'
        noteAddedBy:
          type: string
          description: Email of the author
        noteAddedAt:
          type: string
          format: date-time
          description: ISO 8601 date-time
        elements:
          anyOf:
            - type: array
              items:
                type: object
                properties:
                  type:
                    type: string
                  value:
                    type: string
                  userObject:
                    anyOf:
                      - $ref: '#/components/schemas/Contact'
                      - type: 'null'
            - type: 'null'
        noteReadDetails:
          anyOf:
            - type: array
              items:
                type: object
                properties:
                  readAt:
                    type: string
                    format: date-time
                    description: ISO 8601 date-time
                  readBy:
                    type: string
                  readByContact:
                    anyOf:
                      - $ref: '#/components/schemas/Contact'
                      - type: 'null'
            - type: 'null'
        noteAddedByContact:
          anyOf:
            - $ref: '#/components/schemas/Contact'
            - type: 'null'
        noteType:
          anyOf:
            - type: string
            - type: 'null'
        noteAddedByType:
          anyOf:
            - type: string
            - type: 'null'
        caption:
          anyOf:
            - type: string
            - type: 'null'
        isNoteAwaitingResponse:
          anyOf:
            - type: boolean
            - type: 'null'
      required:
        - text
        - noteAddedBy
        - noteAddedAt
      description: >-
        Work-order note (abridged - additional media/caption/transcription
        fields exist).
      additionalProperties: true
    FileDetails:
      type: object
      properties:
        fileName:
          type: string
          description: Original file name (non-ASCII characters removed on upload)
        fileId:
          type: string
          description: Server-assigned file identifier; use with the file download endpoint
      required:
        - fileName
        - fileId
      description: >-
        Uploaded-file reference, as returned by the file upload endpoint and
        used in all attachment fields.
    SimpleLineItem:
      type: object
      properties:
        description:
          type: string
        amount:
          type: string
      required:
        - description
        - amount
    Contact:
      type: object
      properties:
        email:
          type: string
        nameGiven:
          type: string
        nameFamily:
          type: string
        title:
          anyOf:
            - type: string
            - type: 'null'
        department:
          anyOf:
            - type: string
            - type: 'null'
        facilityId:
          anyOf:
            - type: integer
            - type: 'null'
        contactType:
          type: string
          description: '"buyer" or "supplier"'
        isSharedContact:
          type: boolean
        readOnlyAccess:
          anyOf:
            - type: boolean
            - type: 'null'
        invitedByEmail:
          anyOf:
            - type: string
            - type: 'null'
        invitedAt:
          anyOf:
            - type: string
              format: date-time
              description: ISO 8601 date-time
            - type: 'null'
        epaCertificationType:
          anyOf:
            - type: string
            - type: 'null'
        epaCertificationNumber:
          anyOf:
            - type: string
            - type: 'null'
        createdAt:
          anyOf:
            - type: string
              format: date-time
              description: ISO 8601 date-time
            - type: 'null'
        updatedAt:
          anyOf:
            - type: string
              format: date-time
              description: ISO 8601 date-time
            - type: 'null'
      required:
        - email
        - nameGiven
        - nameFamily
        - contactType
      description: >-
        Contact record (abridged - the full Contact model carries additional
        fields).
      additionalProperties: true
  securitySchemes:
    ApiKeyAuth:
      type: apiKey
      in: header
      name: X-API-KEY
    OwKeyAuth:
      type: apiKey
      in: header
      name: OW-KEY
      description: >-
        OpenWrench shared secret. Required on every request alongside X-API-KEY;
        issued together with your API key.

````