{
  "openapi": "3.1.0",
  "info": {
    "title": "One Page Invoice API",
    "version": "1.0.0",
    "summary": "Make one-page invoices: a PDF, or a shareable page with a Pay button.",
    "description": "No account or key. JSON in; JSON, SVG or PDF out. Guide: https://onepageinvoice.com/docs/api",
    "contact": {
      "email": "hello@onepageinvoice.com",
      "url": "https://onepageinvoice.com/docs/api"
    }
  },
  "servers": [
    {
      "url": "https://onepageinvoice.com"
    }
  ],
  "paths": {
    "/api/v1/invoices": {
      "post": {
        "operationId": "createInvoice",
        "summary": "Save an invoice behind a share link (30 days)",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/Invoice"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Saved",
            "headers": {
              "Location": {
                "schema": {
                  "type": "string"
                },
                "description": "The share page"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Saved"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "413": {
            "$ref": "#/components/responses/TooLarge"
          },
          "422": {
            "$ref": "#/components/responses/Problems"
          },
          "429": {
            "$ref": "#/components/responses/TooMany"
          }
        }
      }
    },
    "/api/v1/invoices/{id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/ID"
        }
      ],
      "get": {
        "operationId": "getInvoice",
        "summary": "Load a saved invoice for editing",
        "security": [
          {
            "editToken": []
          }
        ],
        "responses": {
          "200": {
            "description": "The invoice and its links",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Saved"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "410": {
            "$ref": "#/components/responses/Error"
          }
        }
      },
      "put": {
        "operationId": "updateInvoice",
        "summary": "Replace a saved invoice; the link stays and its 30 days restart",
        "security": [
          {
            "editToken": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/Invoice"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Saved"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "410": {
            "$ref": "#/components/responses/Error"
          },
          "422": {
            "$ref": "#/components/responses/Problems"
          },
          "429": {
            "$ref": "#/components/responses/TooMany"
          }
        }
      },
      "delete": {
        "operationId": "disableInvoice",
        "summary": "Turn the link off and erase the invoice",
        "security": [
          {
            "editToken": []
          }
        ],
        "responses": {
          "204": {
            "description": "Turned off; the share page now answers 410"
          },
          "401": {
            "$ref": "#/components/responses/Error"
          },
          "403": {
            "$ref": "#/components/responses/Error"
          },
          "404": {
            "$ref": "#/components/responses/Error"
          },
          "410": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/api/v1/pdf": {
      "post": {
        "operationId": "renderPDF",
        "summary": "Render a PDF without storing anything",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/Invoice"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The PDF (attachment)",
            "content": {
              "application/pdf": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "413": {
            "$ref": "#/components/responses/TooLarge"
          },
          "422": {
            "$ref": "#/components/responses/Problems"
          }
        }
      }
    },
    "/api/v1/preview": {
      "post": {
        "operationId": "previewInvoice",
        "summary": "Draw a draft as SVG with per-field problems (the editor's live preview)",
        "parameters": [
          {
            "name": "saved",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "ID of a saved invoice: include its QR code and link"
          },
          {
            "name": "link",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "new"
              ]
            },
            "description": "Reserve the QR code before the first save"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/Invoice"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Preview",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Preview"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "413": {
            "$ref": "#/components/responses/TooLarge"
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "editToken": {
        "type": "http",
        "scheme": "bearer",
        "description": "The edit_token returned when the invoice was created"
      }
    },
    "parameters": {
      "ID": {
        "name": "id",
        "in": "path",
        "required": true,
        "schema": {
          "type": "string",
          "pattern": "^[a-z2-7]{16}$"
        }
      }
    },
    "responses": {
      "Error": {
        "description": "Error",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "BadRequest": {
        "description": "Malformed JSON or an unknown field",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "TooLarge": {
        "description": "Body over 3 MB",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "TooMany": {
        "description": "Too many saves; retry after the Retry-After header",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "Problems": {
        "description": "The invoice has problems, listed per field",
        "content": {
          "application/json": {
            "schema": {
              "type": "object",
              "required": [
                "problems"
              ],
              "properties": {
                "problems": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/Problem"
                  }
                }
              }
            }
          }
        }
      }
    },
    "schemas": {
      "Invoice": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "number": {
            "type": "string",
            "maxLength": 32,
            "description": "Required"
          },
          "date": {
            "type": [
              "string",
              "null"
            ],
            "format": "date",
            "description": "Required, YYYY-MM-DD"
          },
          "due_date": {
            "type": [
              "string",
              "null"
            ],
            "format": "date",
            "description": "Defaults to date"
          },
          "currency": {
            "type": "string",
            "enum": [
              "USD",
              "EUR",
              "GBP",
              "CAD",
              "AUD",
              "NZD",
              "CHF",
              "JPY"
            ],
            "default": "USD"
          },
          "from": {
            "$ref": "#/components/schemas/Sender"
          },
          "to": {
            "$ref": "#/components/schemas/Client"
          },
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Item"
            },
            "maxItems": 15,
            "description": "At least one. Classic and Bold fit 15, Minimal 10."
          },
          "tax_rate": {
            "type": "number",
            "minimum": 0,
            "maximum": 100,
            "description": "Percent of the net amount"
          },
          "payment_instructions": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "maxItems": 4,
            "description": "Lines, e.g. bank details"
          },
          "payment_url": {
            "type": "string",
            "format": "uri",
            "pattern": "^https://",
            "description": "Behind the Pay button and 'Pay online'"
          },
          "link": {
            "type": "boolean",
            "default": false,
            "description": "Print the address of the online invoice (saved invoices only): 'Pay online: …' or 'View online: …'."
          },
          "qr": {
            "type": "boolean",
            "default": false,
            "description": "Print a QR code that opens the online invoice (saved invoices only). Independent of link."
          },
          "notes": {
            "type": "string",
            "maxLength": 400,
            "description": "At most four lines on the page"
          },
          "signature": {
            "$ref": "#/components/schemas/Signature"
          },
          "template": {
            "type": "string",
            "enum": [
              "classic",
              "minimal",
              "bold"
            ],
            "default": "classic"
          },
          "accent": {
            "type": "string",
            "pattern": "^#[0-9a-fA-F]{6}$",
            "default": "#5980a6"
          },
          "page_size": {
            "type": "string",
            "enum": [
              "letter",
              "a4"
            ],
            "default": "letter"
          }
        }
      },
      "Sender": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "name": {
            "type": "string",
            "description": "Required"
          },
          "address": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "maxItems": 4
          },
          "email": {
            "type": "string"
          },
          "phone": {
            "type": "string"
          },
          "website": {
            "type": "string"
          },
          "tax_id": {
            "type": "string"
          },
          "logo": {
            "type": "string",
            "contentEncoding": "base64",
            "description": "PNG or JPEG, at most 1 MB (Classic prints it)"
          }
        }
      },
      "Client": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "name": {
            "type": "string",
            "description": "Required"
          },
          "lines": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "maxItems": 4,
            "description": "Attention line and address"
          },
          "email": {
            "type": "string"
          }
        }
      },
      "Item": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "description",
          "quantity",
          "unit_price"
        ],
        "properties": {
          "description": {
            "type": "string",
            "maxLength": 200
          },
          "quantity": {
            "type": "number",
            "exclusiveMinimum": 0
          },
          "unit_price": {
            "type": "number",
            "description": "Negative for a discount or advance"
          }
        }
      },
      "Signature": {
        "type": "object",
        "additionalProperties": false,
        "description": "Optional signature line (Classic)",
        "properties": {
          "image": {
            "type": "string",
            "contentEncoding": "base64"
          },
          "label": {
            "type": "string",
            "default": "Authorised signature"
          }
        }
      },
      "Saved": {
        "type": "object",
        "required": [
          "id",
          "url",
          "pdf_url"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "url": {
            "type": "string",
            "format": "uri",
            "description": "Share page for the client"
          },
          "pdf_url": {
            "type": "string",
            "format": "uri"
          },
          "edit_url": {
            "type": "string",
            "format": "uri",
            "description": "Private: opens the invoice in the editor"
          },
          "edit_token": {
            "type": "string",
            "description": "Only in the create response. Keep it secret."
          },
          "expires_at": {
            "type": "string",
            "format": "date-time"
          },
          "invoice": {
            "$ref": "#/components/schemas/Invoice",
            "description": "Only on GET"
          }
        }
      },
      "Preview": {
        "type": "object",
        "required": [
          "problems",
          "fits"
        ],
        "properties": {
          "svg": {
            "type": "string"
          },
          "fits": {
            "type": "boolean"
          },
          "problems": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Problem"
            }
          },
          "totals": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            }
          }
        }
      },
      "Problem": {
        "type": "object",
        "required": [
          "field",
          "message"
        ],
        "properties": {
          "field": {
            "type": "string",
            "description": "JSON path, e.g. to.name or items.2.unit_price; empty for the whole invoice"
          },
          "message": {
            "type": "string"
          },
          "missing": {
            "type": "boolean",
            "description": "A required field that is still empty"
          }
        }
      },
      "Error": {
        "type": "object",
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "type": "string"
          }
        }
      }
    }
  }
}
