{
  "openapi": "3.1.0",
  "info": {
    "title": "InvoiceJet API",
    "version": "1.0.0",
    "description": "Extract verified structured data from invoices (PDF, image, or HTML). Async by default with completion webhooks; add wait=true for synchronous extraction. Docs: https://app.invoicejet.ai/docs"
  },
  "servers": [
    { "url": "https://nzsktgwehyzltgagloox.supabase.co/functions/v1/api" }
  ],
  "security": [{ "bearerAuth": [] }],
  "paths": {
    "/v1/invoices": {
      "post": {
        "summary": "Extract an invoice",
        "operationId": "createInvoice",
        "parameters": [
          {
            "name": "wait",
            "in": "query",
            "schema": { "type": "boolean", "default": false },
            "description": "Hold the request (up to 60s) and return the finished resource."
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "schema": { "type": "string", "maxLength": 255 },
            "description": "Makes the create safe to retry: the same key within 24 hours replays the original response (Idempotency-Replayed: true header) instead of ingesting again. 409 idempotency_in_flight while the original is still processing."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "required": ["file"],
                "properties": { "file": { "type": "string", "format": "binary" } }
              }
            },
            "application/json": {
              "schema": {
                "oneOf": [
                  {
                    "type": "object",
                    "required": ["file_url"],
                    "properties": {
                      "file_url": { "type": "string", "format": "uri", "description": "Public https URL; fetched with a 20s timeout and 15 MB cap." },
                      "file_name": { "type": "string" },
                      "mime_type": { "type": "string" }
                    }
                  },
                  {
                    "type": "object",
                    "required": ["file_base64", "file_name"],
                    "properties": {
                      "file_base64": { "type": "string" },
                      "file_name": { "type": "string" },
                      "mime_type": { "type": "string" }
                    }
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": { "description": "Extraction finished (wait=true).", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Invoice" } } } },
          "202": { "description": "Accepted; extraction continues in the background.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Invoice" } } } },
          "400": { "$ref": "#/components/responses/Error" },
          "401": { "$ref": "#/components/responses/Error" },
          "409": { "$ref": "#/components/responses/Error" },
          "413": { "$ref": "#/components/responses/Error" },
          "415": { "$ref": "#/components/responses/Error" },
          "422": { "$ref": "#/components/responses/Error" },
          "429": { "$ref": "#/components/responses/Error" }
        }
      },
      "get": {
        "summary": "List invoices",
        "operationId": "listInvoices",
        "parameters": [
          { "name": "limit", "in": "query", "schema": { "type": "integer", "minimum": 1, "maximum": 100, "default": 25 } },
          { "name": "status", "in": "query", "schema": { "type": "string", "enum": ["queued", "processing", "complete", "failed"] } },
          { "name": "source", "in": "query", "schema": { "type": "string", "enum": ["app", "email", "api"] } },
          { "name": "created_after", "in": "query", "schema": { "type": "string", "format": "date-time" } },
          { "name": "created_before", "in": "query", "schema": { "type": "string", "format": "date-time" } },
          { "name": "vendor", "in": "query", "schema": { "type": "string" }, "description": "Substring match on vendor name." },
          { "name": "paid", "in": "query", "schema": { "type": "boolean" } },
          { "name": "overdue", "in": "query", "schema": { "type": "boolean" }, "description": "true: complete, unpaid, past due." },
          { "name": "min_amount", "in": "query", "schema": { "type": "number" } },
          { "name": "max_amount", "in": "query", "schema": { "type": "number" } },
          { "name": "due_before", "in": "query", "schema": { "type": "string", "format": "date" } },
          { "name": "due_after", "in": "query", "schema": { "type": "string", "format": "date" } },
          { "name": "starting_after", "in": "query", "schema": { "type": "string", "format": "uuid" }, "description": "Keyset cursor: results strictly after this invoice id (newest first)." }
        ],
        "responses": {
          "200": { "description": "A page of invoices.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/InvoiceList" } } } },
          "400": { "$ref": "#/components/responses/Error" },
          "401": { "$ref": "#/components/responses/Error" },
          "429": { "$ref": "#/components/responses/Error" }
        }
      }
    },
    "/v1/invoices/{id}": {
      "get": {
        "summary": "Retrieve an invoice",
        "operationId": "getInvoice",
        "parameters": [
          { "name": "id", "in": "path", "required": true, "schema": { "type": "string", "format": "uuid" } }
        ],
        "responses": {
          "200": { "description": "The invoice.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Invoice" } } } },
          "401": { "$ref": "#/components/responses/Error" },
          "404": { "$ref": "#/components/responses/Error" },
          "429": { "$ref": "#/components/responses/Error" }
        }
      },
      "patch": {
        "summary": "Update an invoice (mark paid, set entity tag)",
        "operationId": "updateInvoice",
        "parameters": [
          { "name": "id", "in": "path", "required": true, "schema": { "type": "string", "format": "uuid" } }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "minProperties": 1,
                "properties": {
                  "paid": { "type": "boolean", "description": "Mark paid/unpaid. Complete invoices only." },
                  "entity_tag": { "type": ["string", "null"], "maxLength": 60, "description": "Entity/company tag; null clears it." }
                }
              }
            }
          }
        },
        "responses": {
          "200": { "description": "The updated invoice.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Invoice" } } } },
          "400": { "$ref": "#/components/responses/Error" },
          "401": { "$ref": "#/components/responses/Error" },
          "404": { "$ref": "#/components/responses/Error" },
          "429": { "$ref": "#/components/responses/Error" }
        }
      }
    },
    "/v1/invoices/{id}/approval": {
      "post": {
        "summary": "Email an approve/reject request for a complete invoice",
        "operationId": "requestApproval",
        "parameters": [
          { "name": "id", "in": "path", "required": true, "schema": { "type": "string", "format": "uuid" } }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["email"],
                "properties": { "email": { "type": "string", "format": "email" } }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Request sent.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": { "type": "string", "format": "uuid" },
                    "approval_status": { "type": "string", "const": "requested" },
                    "approval_email": { "type": "string" }
                  }
                }
              }
            }
          },
          "400": { "$ref": "#/components/responses/Error" },
          "401": { "$ref": "#/components/responses/Error" },
          "404": { "$ref": "#/components/responses/Error" },
          "429": { "$ref": "#/components/responses/Error" },
          "502": { "$ref": "#/components/responses/Error" }
        }
      }
    },
    "/v1/invoices/{id}/file": {
      "get": {
        "summary": "Get a signed download URL for the original document",
        "operationId": "getInvoiceFile",
        "parameters": [
          { "name": "id", "in": "path", "required": true, "schema": { "type": "string", "format": "uuid" } }
        ],
        "responses": {
          "200": {
            "description": "Signed URL, valid for one hour.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": { "type": "string", "format": "uuid" },
                    "url": { "type": "string", "format": "uri" },
                    "expires_in": { "type": "integer", "const": 3600 },
                    "file_name": { "type": ["string", "null"] },
                    "mime_type": { "type": ["string", "null"] }
                  }
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Error" },
          "404": { "$ref": "#/components/responses/Error" },
          "429": { "$ref": "#/components/responses/Error" }
        }
      }
    },
    "/v1/invoices/{id}/extract": {
      "post": {
        "summary": "Re-run extraction on the stored document",
        "operationId": "retryExtraction",
        "parameters": [
          { "name": "id", "in": "path", "required": true, "schema": { "type": "string", "format": "uuid" } }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "force": { "type": "boolean", "default": false, "description": "Re-run even if the invoice is already complete." }
                }
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Retry accepted; the invoice returns to queued state and re-extracts in the background.",
            "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Invoice" } } }
          },
          "400": { "$ref": "#/components/responses/Error" },
          "401": { "$ref": "#/components/responses/Error" },
          "404": { "$ref": "#/components/responses/Error" },
          "429": { "$ref": "#/components/responses/Error" }
        }
      }
    },
    "/v1/vendors": {
      "get": {
        "summary": "List vendors with aggregates (highest spend first)",
        "operationId": "listVendors",
        "parameters": [
          { "name": "limit", "in": "query", "schema": { "type": "integer", "minimum": 1, "maximum": 100, "default": 25 } }
        ],
        "responses": {
          "200": {
            "description": "Vendors.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "object": { "type": "string", "const": "list" },
                    "data": { "type": "array", "items": { "$ref": "#/components/schemas/Vendor" } }
                  }
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Error" },
          "429": { "$ref": "#/components/responses/Error" }
        }
      }
    },
    "/v1/events": {
      "get": {
        "summary": "List invoice events (webhook payloads, newest first)",
        "operationId": "listEvents",
        "parameters": [
          { "name": "limit", "in": "query", "schema": { "type": "integer", "minimum": 1, "maximum": 100, "default": 25 } },
          { "name": "type", "in": "query", "schema": { "type": "string", "enum": ["invoice.completed", "invoice.failed"] } },
          { "name": "created_after", "in": "query", "schema": { "type": "string", "format": "date-time" } },
          { "name": "created_before", "in": "query", "schema": { "type": "string", "format": "date-time" } },
          { "name": "starting_after", "in": "query", "schema": { "type": "string" }, "description": "Event id cursor (evt_...): results strictly after it (newest first)." }
        ],
        "responses": {
          "200": {
            "description": "A page of events; each item is the exact webhook payload.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "object": { "type": "string", "const": "list" },
                    "data": { "type": "array", "items": { "$ref": "#/components/schemas/WebhookEvent" } },
                    "has_more": { "type": "boolean" }
                  }
                }
              }
            }
          },
          "400": { "$ref": "#/components/responses/Error" },
          "401": { "$ref": "#/components/responses/Error" },
          "429": { "$ref": "#/components/responses/Error" }
        }
      }
    },
    "/v1/me": {
      "get": {
        "summary": "Verify the API key and read quota",
        "operationId": "getMe",
        "responses": {
          "200": {
            "description": "Key metadata and monthly quota.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "user_id": { "type": "string", "format": "uuid" },
                    "key": {
                      "type": "object",
                      "properties": {
                        "id": { "type": "string", "format": "uuid" },
                        "name": { "type": "string" },
                        "prefix": { "type": "string" },
                        "created_at": { "type": "string", "format": "date-time" }
                      }
                    },
                    "quota": {
                      "type": "object",
                      "properties": {
                        "used": { "type": "integer" },
                        "limit": { "type": ["integer", "null"] },
                        "period_start": { "type": "string", "format": "date-time" }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Error" }
        }
      }
    }
  },
  "webhooks": {
    "invoice.completed": {
      "post": {
        "summary": "Extraction finished",
        "description": "Signed with your endpoint's whsec_ secret. Headers: webhook-id, webhook-timestamp (unix seconds), webhook-signature (v1,<base64 HMAC-SHA256 of '{id}.{timestamp}.{body}'>).",
        "requestBody": {
          "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WebhookEvent" } } }
        },
        "responses": { "200": { "description": "Acknowledge with any 2xx within 10 seconds." } }
      }
    },
    "invoice.failed": {
      "post": {
        "summary": "Extraction failed",
        "requestBody": {
          "content": { "application/json": { "schema": { "$ref": "#/components/schemas/WebhookEvent" } } }
        },
        "responses": { "200": { "description": "Acknowledge with any 2xx within 10 seconds." } }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "ijk_live_...",
        "description": "API key from app.invoicejet.ai/app/developers. Keys have a scope: 'full' or 'read' (GET endpoints only; writes return 403 insufficient_scope)."
      }
    },
    "responses": {
      "Error": {
        "description": "Error envelope.",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }
      }
    },
    "schemas": {
      "Invoice": {
        "type": "object",
        "properties": {
          "id": { "type": "string", "format": "uuid" },
          "object": { "type": "string", "const": "invoice" },
          "status": { "type": "string", "enum": ["queued", "processing", "complete", "failed"] },
          "source": { "type": "string", "enum": ["app", "email", "api"] },
          "auto_verified": { "type": "boolean" },
          "quality_score": { "type": ["number", "null"], "minimum": 0, "maximum": 1 },
          "created_at": { "type": ["string", "null"], "format": "date-time" },
          "updated_at": { "type": ["string", "null"], "format": "date-time" },
          "file": {
            "type": "object",
            "properties": {
              "name": { "type": ["string", "null"] },
              "mime_type": { "type": ["string", "null"] },
              "size_bytes": { "type": ["integer", "null"] }
            }
          },
          "vendor_name": { "type": ["string", "null"] },
          "invoice_number": { "type": ["string", "null"] },
          "amount": { "type": ["number", "null"] },
          "currency": { "type": ["string", "null"] },
          "issued_at": { "type": ["string", "null"], "format": "date" },
          "due_at": { "type": ["string", "null"], "format": "date" },
          "tax": { "type": ["number", "null"] },
          "line_items": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "description": { "type": ["string", "null"] },
                "quantity": { "type": ["number", "null"] },
                "unit_price": { "type": ["number", "null"] },
                "confidence": { "type": ["string", "null"], "enum": ["high", "mid", "low", null] }
              }
            }
          },
          "field_confidence": {
            "type": ["object", "null"],
            "additionalProperties": { "type": "string", "enum": ["high", "mid", "low"] }
          },
          "wait_timed_out": { "type": "boolean", "description": "Present on 202 responses to wait=true when extraction outran the wait window." }
        }
      },
      "Vendor": {
        "type": "object",
        "properties": {
          "id": { "type": "string", "format": "uuid" },
          "object": { "type": "string", "const": "vendor" },
          "name": { "type": "string" },
          "invoice_count": { "type": "integer" },
          "total_billed": { "type": "number" },
          "last_seen_at": { "type": ["string", "null"], "format": "date" },
          "website": { "type": ["string", "null"] },
          "email": { "type": ["string", "null"] },
          "phone": { "type": ["string", "null"] }
        }
      },
      "InvoiceList": {
        "type": "object",
        "properties": {
          "object": { "type": "string", "const": "list" },
          "data": { "type": "array", "items": { "$ref": "#/components/schemas/Invoice" } },
          "has_more": { "type": "boolean" }
        }
      },
      "WebhookEvent": {
        "type": "object",
        "properties": {
          "id": { "type": "string", "description": "evt_<uuid>" },
          "type": { "type": "string", "enum": ["invoice.completed", "invoice.failed"] },
          "created_at": { "type": "string", "format": "date-time" },
          "data": {
            "type": "object",
            "properties": { "invoice": { "$ref": "#/components/schemas/Invoice" } }
          }
        }
      },
      "Error": {
        "type": "object",
        "properties": {
          "error": {
            "type": "object",
            "properties": {
              "type": {
                "type": "string",
                "enum": ["invalid_request_error", "authentication_error", "not_found", "rate_limit_error", "quota_exceeded", "api_error"]
              },
              "code": { "type": "string" },
              "message": { "type": "string" }
            }
          }
        }
      }
    }
  }
}
