{
 "openapi": "3.1.0",
 "info": {
  "title": "Scale Up Books API",
  "version": "1.0.0",
  "description": "External REST API for Scale Up Books (the Scale Up Ledger). Authenticate every request with an organisation-scoped API key in the X-Api-Key header. Keys are issued and revoked in Books under Settings > API keys. Some endpoints need a key with reporting or dashboard-read scope."
 },
 "servers": [
  {
   "url": "https://books.scaleupsolutions.asia/api/v1/external"
  }
 ],
 "security": [
  {
   "ApiKeyAuth": []
  }
 ],
 "paths": {
  "/contacts": {
   "get": {
    "summary": "List Contacts",
    "description": "starts_with is for a flow that only knows a stable short code, not\nthe contact's id -- SEVEA's project codes are the start of a\nproject's own name (a contact with parent_contact_id set, e.g.\n\"1527_CAPRED\" under parent \"CAPRED\" -- the same sub-customer/job\nconcept as QBO's Customer:Job, its old flow's actual lookup target\ndespite reading as a plain \"customer\" search). include_projects (added\nfor the Power BI dashboard migration -- Joe: \"this one should load\ncustomer and project, with the parent column too\") is the same broader\n\"every contact regardless of parent\" search with no prefix required,\nfor a caller that wants the whole customer+project tree in one flat\npull rather than a name-prefix lookup. A plain listing/q search (no\nstarts_with, include_projects=false) stays scoped to top-level contacts\nonly -- unchanged, other callers rely on that for a flat customer\npicker. Either broader path includes parent_name so a caller can tell\nwhich company a project belongs to, same as the web app's own\n/web/projects search (_projects_for_org in routes_arap.py).\n\ntype=\"customer\"/\"supplier\" also matches type=\"both\" contacts (a \"both\"\ncontact is a customer AND a supplier -- belongs in either list, same\nrule GET /web/arap's own contact list applies, routes_arap.py:184-187).\ntype=\"employee\" (or any other exact type) matches only that type.",
    "operationId": "list_contacts_api_v1_external_contacts_get",
    "parameters": [
     {
      "name": "type",
      "in": "query",
      "required": false,
      "schema": {
       "type": "string",
       "default": "customer",
       "title": "Type"
      }
     },
     {
      "name": "q",
      "in": "query",
      "required": false,
      "schema": {
       "anyOf": [
        {
         "type": "string"
        },
        {
         "type": "null"
        }
       ],
       "title": "Q"
      }
     },
     {
      "name": "starts_with",
      "in": "query",
      "required": false,
      "schema": {
       "anyOf": [
        {
         "type": "string"
        },
        {
         "type": "null"
        }
       ],
       "title": "Starts With"
      }
     },
     {
      "name": "include_projects",
      "in": "query",
      "required": false,
      "schema": {
       "type": "boolean",
       "default": false,
       "title": "Include Projects"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "Successful Response",
      "content": {
       "application/json": {
        "schema": {}
       }
      }
     },
     "422": {
      "description": "Validation Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      }
     }
    },
    "tags": [
     "contacts"
    ]
   }
  },
  "/contacts/{contact_id}": {
   "get": {
    "summary": "Get Contact",
    "description": "Direct by-id lookup -- GET /contacts (list, capped and search-only)\ncan't reliably resolve one specific contact a caller already has the\nid for (e.g. a bot's /ar-aging row, resolving who to email a chase\nto).",
    "operationId": "get_contact_api_v1_external_contacts__contact_id__get",
    "parameters": [
     {
      "name": "contact_id",
      "in": "path",
      "required": true,
      "schema": {
       "type": "string",
       "format": "uuid",
       "title": "Contact Id"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "Successful Response",
      "content": {
       "application/json": {
        "schema": {}
       }
      }
     },
     "422": {
      "description": "Validation Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      }
     }
    },
    "tags": [
     "contacts"
    ]
   }
  },
  "/contacts/find-or-create": {
   "post": {
    "summary": "Find Or Create Contact",
    "description": "Mirrors QBO's ensure_customer/ensure_subcustomer -- the bot fleet's\nposting flow always resolves a vendor/customer/project by name before\ndeciding whether to create it, so a slightly retyped name each run\ndoesn't pile up duplicate contacts. Matches on name_en case-insensitively,\nscoped to the same parent (or same top-level type, for a non-project).",
    "operationId": "find_or_create_contact_api_v1_external_contacts_find_or_create_post",
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/ContactFindOrCreate"
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Successful Response",
      "content": {
       "application/json": {
        "schema": {}
       }
      }
     },
     "422": {
      "description": "Validation Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      }
     }
    },
    "tags": [
     "contacts"
    ]
   }
  },
  "/contacts/extract-patent": {
   "post": {
    "summary": "Extract Patent",
    "description": "Ported from estimates-bot's own /newcustomerpatent (Joe: \"I have a\ncommand in the estimate bot that can create a customer based on the\npatent tax certificate, can we add this in to the create contact?\" --\nalready built for the web UI's own Create Contact form, see\napp/services/patent_extract.py; this exposes the same OCR/extraction\nstep to the bot fleet). Extraction only -- the caller reviews the\nfields (same confirmation step the bot's own command already shows)\nthen calls POST /contacts/find-or-create with them to actually create\nthe contact. No DB write happens here (just an OCR call, no org_id\nactually used) but it's still gated behind a valid API key -- an\nunauthenticated caller shouldn't be able to burn this org's Gemini\nquota on arbitrary documents.",
    "operationId": "extract_patent_api_v1_external_contacts_extract_patent_post",
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/PatentExtractRequest"
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Successful Response",
      "content": {
       "application/json": {
        "schema": {}
       }
      }
     },
     "422": {
      "description": "Validation Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      }
     }
    },
    "tags": [
     "contacts"
    ]
   }
  },
  "/projects": {
   "get": {
    "summary": "List Projects",
    "description": "include_activity adds has_estimate/has_invoice per project (a bot's\nstock-out project picker warns rather than blocks when either is\nmissing, Joe: \"if no invoice or estimate on the project we want to\nthrow the are you sure section\") -- two org-wide distinct-contact_id\nqueries, not one per project, so this stays cheap even at EGE's real\n564-project scale.",
    "operationId": "list_projects_api_v1_external_projects_get",
    "parameters": [
     {
      "name": "q",
      "in": "query",
      "required": false,
      "schema": {
       "anyOf": [
        {
         "type": "string"
        },
        {
         "type": "null"
        }
       ],
       "title": "Q"
      }
     },
     {
      "name": "include_activity",
      "in": "query",
      "required": false,
      "schema": {
       "type": "boolean",
       "default": false,
       "title": "Include Activity"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "Successful Response",
      "content": {
       "application/json": {
        "schema": {}
       }
      }
     },
     "422": {
      "description": "Validation Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      }
     }
    },
    "tags": [
     "projects"
    ]
   },
   "post": {
    "summary": "Create Project",
    "operationId": "create_project_api_v1_external_projects_post",
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/ProjectCreate"
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Successful Response",
      "content": {
       "application/json": {
        "schema": {}
       }
      }
     },
     "422": {
      "description": "Validation Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      }
     }
    },
    "tags": [
     "projects"
    ]
   }
  },
  "/items": {
   "get": {
    "summary": "List Items",
    "description": "side='sales' for invoice/estimate lines, 'purchase' for bill lines --\nsame picker query the web line-item forms use (app/web/routes_arap.py's\n_items_for_picker), so a caller resolving a QBO-style Item by name/code\nsees exactly what a human would in the dropdown.",
    "operationId": "list_items_api_v1_external_items_get",
    "parameters": [
     {
      "name": "side",
      "in": "query",
      "required": false,
      "schema": {
       "type": "string",
       "default": "sales",
       "title": "Side"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "Successful Response",
      "content": {
       "application/json": {
        "schema": {}
       }
      }
     },
     "422": {
      "description": "Validation Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      }
     }
    },
    "tags": [
     "items"
    ]
   }
  },
  "/streams": {
   "get": {
    "summary": "List Streams",
    "description": "Resolve a real stream_id for POST /invoices and POST /estimates/{id}/\nconvert -- a client's \"tax\" (VAT-standard) vs \"commercial\" (out-of-scope)\nstream changes VAT-return inclusion, so this API never guesses one on\nthe caller's behalf. Expected to be called once per client at bot\nonboarding, not per transaction -- a stream rarely changes.\n\nJoe, 2026-10-01: \"small businesses... only have commercial invoice\nand not tax... when small business is used then there is no option\nfor tax invoice.\" A Small Business org (Org.is_small_business) isn't\non the standard VAT regime for its own SALES either -- a standard-\nrate (\"tax\") invoice stream is never a real choice for them, so it's\nfiltered out of every live caller's picker (Mini App, bot) rather\nthan relying on each one to know this rule itself.",
    "operationId": "list_streams_api_v1_external_streams_get",
    "parameters": [
     {
      "name": "document_type",
      "in": "query",
      "required": false,
      "schema": {
       "type": "string",
       "default": "invoice",
       "title": "Document Type"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "Successful Response",
      "content": {
       "application/json": {
        "schema": {}
       }
      }
     },
     "422": {
      "description": "Validation Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      }
     }
    },
    "tags": [
     "streams"
    ]
   }
  },
  "/invoice-templates": {
   "get": {
    "summary": "List Invoice Templates",
    "description": "Resolve a real template_id to pass as InvoiceCreate.template_id --\nfor orgs like MyTeb where the choice of layout (e.g. Branded vs\nUnbranded) is a per-invoice decision the caller makes, independent of\nthe stream's own default template.",
    "operationId": "list_invoice_templates_api_v1_external_invoice_templates_get",
    "responses": {
     "200": {
      "description": "Successful Response",
      "content": {
       "application/json": {
        "schema": {}
       }
      }
     },
     "422": {
      "description": "Validation Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      }
     }
    },
    "tags": [
     "invoice-templates"
    ]
   }
  },
  "/org-info": {
   "get": {
    "summary": "Org Info Route",
    "description": "Joe, 2026-09-30: \"somewhere in the ledger that we can write the\nprompt too that it looks up.\" bot_prompt is a short, org-owned\ninstruction (Settings > Bot Assistant, <=200 chars) a bot's own\nAI-driven step (vendor/category mapping, quote classification, ...)\ncan fold into whatever it's deciding -- empty string when the org\nhasn't set one, never null, so a caller can always safely\nconcatenate it into a larger prompt with no extra branching.",
    "operationId": "org_info_route_api_v1_external_org_info_get",
    "responses": {
     "200": {
      "description": "Successful Response",
      "content": {
       "application/json": {
        "schema": {}
       }
      }
     },
     "422": {
      "description": "Validation Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      }
     }
    },
    "tags": [
     "org-info"
    ]
   }
  },
  "/fx-rate": {
   "get": {
    "summary": "Fx Rate Route",
    "description": "The global GDT rate effective on or before `on_date` (a weekend/\nholiday returns the prior business day's), with `rate_date` so the\ncaller can see how stale it is. Joe, 2026-10-02: the expense bot was\nscraping tax.gov.kh itself (and silently fell back to a hardcoded rate\nwhen GDT changed its markup) even though the ledger already stores\nevery day -- bots call this instead. Works with a normal scope='full'\nkey; unlike /exchange-rates it needs no reporting add-on, since it\nonly ever returns the global published rate (never an org's own).",
    "operationId": "fx_rate_route_api_v1_external_fx_rate_get",
    "parameters": [
     {
      "name": "on_date",
      "in": "query",
      "required": true,
      "schema": {
       "type": "string",
       "format": "date",
       "title": "On Date"
      }
     },
     {
      "name": "from_ccy",
      "in": "query",
      "required": false,
      "schema": {
       "type": "string",
       "default": "USD",
       "title": "From Ccy"
      }
     },
     {
      "name": "to_ccy",
      "in": "query",
      "required": false,
      "schema": {
       "type": "string",
       "default": "KHR",
       "title": "To Ccy"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "Successful Response",
      "content": {
       "application/json": {
        "schema": {}
       }
      }
     },
     "422": {
      "description": "Validation Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      }
     }
    },
    "tags": [
     "fx-rate"
    ]
   }
  },
  "/accounts": {
   "get": {
    "summary": "List Accounts Route",
    "description": "Resolve a real account_id for POST /invoices/POST /bills line items\n-- a satellite app (Timesheets' own default_income_account_id setting,\nthe CRM's per-org \"which account do won deals invoice against\") picks\none of these once at org setup, not per transaction. type filters to\nincome|expense|asset|liability|equity when given, e.g. type=income\nfor a sales-side default; omitted returns every active account.",
    "operationId": "list_accounts_route_api_v1_external_accounts_get",
    "parameters": [
     {
      "name": "type",
      "in": "query",
      "required": false,
      "schema": {
       "anyOf": [
        {
         "type": "string"
        },
        {
         "type": "null"
        }
       ],
       "title": "Type"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "Successful Response",
      "content": {
       "application/json": {
        "schema": {}
       }
      }
     },
     "422": {
      "description": "Validation Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      }
     }
    },
    "tags": [
     "accounts"
    ]
   }
  },
  "/bank-accounts/balances": {
   "get": {
    "summary": "Bank Account Balances",
    "description": "estimates-bot's /bank -- current GL balance and last transaction date\nper bank account (Joe, 2026-09-21: found while auditing every estimates-\nbot command against Ledger-live tenants before QBO subscriptions get\ncancelled -- /bank was still 100% hardcoded to QBO).",
    "operationId": "bank_account_balances_api_v1_external_bank_accounts_balances_get",
    "responses": {
     "200": {
      "description": "Successful Response",
      "content": {
       "application/json": {
        "schema": {}
       }
      }
     },
     "422": {
      "description": "Validation Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      }
     }
    },
    "tags": [
     "bank-accounts"
    ]
   }
  },
  "/products": {
   "get": {
    "summary": "List Products",
    "description": "Which OTHER products (Timesheets/CRM) this org is active on, for a\nsatellite to know what cross-app nav links to show (Joe, 2026-09-21:\n\"make all have links to all\"). Doesn't include 'ledger' itself --\nevery satellite already stores its own ledger_base_url.",
    "operationId": "list_products_api_v1_external_products_get",
    "responses": {
     "200": {
      "description": "Successful Response",
      "content": {
       "application/json": {
        "schema": {}
       }
      }
     },
     "422": {
      "description": "Validation Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      }
     }
    },
    "tags": [
     "products"
    ]
   }
  },
  "/sso-link": {
   "post": {
    "summary": "Sso Link",
    "description": "Mints a signed SSO handoff token for the CALLING satellite's\nalready-authenticated user to log into a DIFFERENT satellite (Joe:\n\"make all have links to all\") -- e.g. Timesheets asking for a\nCRM-bound link, without needing to know CRM's own external_org_id\nitself; the ledger already has both sides of every OrgProduct link.\nSame trust shape as GET /web/sso/{product} (that route mints for the\nledger's OWN logged-in user via a session cookie; this mints on\nbehalf of a caller that already authenticated its own user locally\n-- the email is trusted because it arrived on that org's own\nX-Api-Key, the same boundary every other external.py write uses).",
    "operationId": "sso_link_api_v1_external_sso_link_post",
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/SsoLinkRequest"
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Successful Response",
      "content": {
       "application/json": {
        "schema": {}
       }
      }
     },
     "422": {
      "description": "Validation Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      }
     }
    },
    "tags": [
     "sso-link"
    ]
   }
  },
  "/tax-rates": {
   "get": {
    "summary": "List Tax Rates Route",
    "description": "Resolve a real tax_rate_id for an InvoiceLineCreate/EstimateLineCreate\n-- GET /items doesn't always carry one (a plain service item often has\nno sales_tax_rate_id of its own set), so a caller building a taxable\nline from scratch needs to look one up directly. kind filters to\nvat|wht|specific|none; applies_to filters to \"sales\" or \"purchases\"\n(matching Account.type's own vocabulary, not a QBO-style boolean).",
    "operationId": "list_tax_rates_route_api_v1_external_tax_rates_get",
    "parameters": [
     {
      "name": "kind",
      "in": "query",
      "required": false,
      "schema": {
       "anyOf": [
        {
         "type": "string"
        },
        {
         "type": "null"
        }
       ],
       "title": "Kind"
      }
     },
     {
      "name": "applies_to",
      "in": "query",
      "required": false,
      "schema": {
       "anyOf": [
        {
         "type": "string"
        },
        {
         "type": "null"
        }
       ],
       "title": "Applies To"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "Successful Response",
      "content": {
       "application/json": {
        "schema": {}
       }
      }
     },
     "422": {
      "description": "Validation Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      }
     }
    },
    "tags": [
     "tax-rates"
    ]
   }
  },
  "/journal-report": {
   "get": {
    "summary": "Journal Report",
    "description": "Flat, all-accounts general-ledger feed for external BI tools -- built\nfor the Power BI dashboards migrating off QBO's own reports/JournalReport\nendpoint (see powerbiMigrate). One row per journal_line across every\naccount in the date range, unlike gl_detail (app/services/reporting.py,\nper-account only -- the web app's own TB drill-down target). Denormalizes\naccount code/name, contact name, and this org's own tracking-dimension\noption names (QBO's Class/Location equivalent, see GET /tracking-categories\nfor what track_1..track_5 mean for THIS org) so a caller doesn't need a\nround trip per row to make it human-readable -- the same shape QBO's\nJournalReport columns=... param used to return, so the M query rewrite\nstays close to the original.\n\nNo draft rows ever appear here -- a draft invoice/bill/estimate has no\njournal at all (post_journal only ever runs at post time), so there's\nnothing to filter for \"draft\". Void is different: voiding NEVER edits or\nremoves the original journal, it posts a second, mirror-image reversal\njournal that nets the original to zero -- so a voided invoice still has\ntwo real, correctly-summing rows here unless you ask to drop them.\n\nis_void detection does NOT rely on Journal.reverses_id -- it's only\npopulated by the generic reverse_journal() helper (bill/payment voids,\na termination payout reversal), NOT by void_invoice's own hand-rolled\nreversal (app/services/invoicing.py), so a reverses_id-only check would\nsilently miss every voided invoice. Instead this matches on source_type\n(every void/reversal source_type in this codebase ends in \"_void\" or\n\"_correction_reversal\", see _VOID_SOURCE_TYPES and reporting.py's own\n_SOURCE_TYPE_LABELS) plus source_id (shared between an original posting\nand whatever later reverses it, regardless of which internal helper\nwrote it). is_void=true on a row means EITHER this row's own journal IS\none of those reversals OR this row's journal has since been reversed by\none that shares its source_id. exclude_void=true drops both halves\nserver-side instead of a caller having to reconstruct that logic in M.\n\nproject_contact_name/project_contact_no resolve JournalLine.\nproject_contact_id -- Joe, 2026-09-29, working out full project costs\nfor the Power BI dashboard: this is deliberately a SEPARATE contact\nfrom contact_id, not the same field QBO's single Customer:Job column\nconflated. A line can have contact_id = the vendor actually paid (or\nnull, for a line with no party at all) and project_contact_id = the\nproject the cost belongs to, at the same time -- e.g. paying a\nsubcontractor for materials on a specific project. Roll up by\nproject_contact_id/project_contact_name for full project cost, not by\ncontact_id/contact_name. project_contact_no (added 2026-10-08) is the\nproject/job number clients key everything by (e.g. \"MYTEB26-0393\"),\nstraight off Contact.contact_no.",
    "operationId": "journal_report_api_v1_external_journal_report_get",
    "parameters": [
     {
      "name": "date_from",
      "in": "query",
      "required": true,
      "schema": {
       "type": "string",
       "format": "date",
       "title": "Date From"
      }
     },
     {
      "name": "date_to",
      "in": "query",
      "required": true,
      "schema": {
       "type": "string",
       "format": "date",
       "title": "Date To"
      }
     },
     {
      "name": "account_id",
      "in": "query",
      "required": false,
      "schema": {
       "anyOf": [
        {
         "type": "string",
         "format": "uuid"
        },
        {
         "type": "null"
        }
       ],
       "title": "Account Id"
      }
     },
     {
      "name": "contact_id",
      "in": "query",
      "required": false,
      "schema": {
       "anyOf": [
        {
         "type": "string",
         "format": "uuid"
        },
        {
         "type": "null"
        }
       ],
       "title": "Contact Id"
      }
     },
     {
      "name": "project_contact_id",
      "in": "query",
      "required": false,
      "schema": {
       "anyOf": [
        {
         "type": "string",
         "format": "uuid"
        },
        {
         "type": "null"
        }
       ],
       "title": "Project Contact Id"
      }
     },
     {
      "name": "exclude_void",
      "in": "query",
      "required": false,
      "schema": {
       "type": "boolean",
       "default": false,
       "title": "Exclude Void"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "Successful Response",
      "content": {
       "application/json": {
        "schema": {}
       }
      }
     },
     "422": {
      "description": "Validation Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      }
     }
    },
    "tags": [
     "journal-report"
    ]
   }
  },
  "/exchange-rates": {
   "get": {
    "summary": "List Exchange Rates",
    "description": "Daily FX rates already captured in this ledger (app.domain.ledger_models.\nFxRate) -- global GDT/NSSF published rates (org_id NULL) plus this org's\nown manual rates. Built for the Power BI dashboard migration, which used\nto scrape tax.gov.kh itself on every refresh (QBO dataflow's FncRatesGDT/\nAllDateRate) -- the ledger already stores exactly this table, so that\nexternal scrape is no longer needed.",
    "operationId": "list_exchange_rates_api_v1_external_exchange_rates_get",
    "parameters": [
     {
      "name": "date_from",
      "in": "query",
      "required": true,
      "schema": {
       "type": "string",
       "format": "date",
       "title": "Date From"
      }
     },
     {
      "name": "date_to",
      "in": "query",
      "required": true,
      "schema": {
       "type": "string",
       "format": "date",
       "title": "Date To"
      }
     },
     {
      "name": "source",
      "in": "query",
      "required": false,
      "schema": {
       "anyOf": [
        {
         "type": "string"
        },
        {
         "type": "null"
        }
       ],
       "title": "Source"
      }
     },
     {
      "name": "from_ccy",
      "in": "query",
      "required": false,
      "schema": {
       "anyOf": [
        {
         "type": "string"
        },
        {
         "type": "null"
        }
       ],
       "title": "From Ccy"
      }
     },
     {
      "name": "to_ccy",
      "in": "query",
      "required": false,
      "schema": {
       "anyOf": [
        {
         "type": "string"
        },
        {
         "type": "null"
        }
       ],
       "title": "To Ccy"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "Successful Response",
      "content": {
       "application/json": {
        "schema": {}
       }
      }
     },
     "422": {
      "description": "Validation Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      }
     }
    },
    "tags": [
     "exchange-rates"
    ]
   }
  },
  "/attachments": {
   "get": {
    "summary": "List Attachments Route",
    "description": "Every scanned-clean, non-archived attachment in this org, with a\nsigned download URL and -- where resolvable -- the journal_id it posted\nto. Built for the Power BI dashboard migration (Joe: \"doesn't need to be\nthe same format [as QBO's BillsAttachments], could just be all\nattachments and the link to the journal id\").\n\nAttachmentLink.entity_type/entity_id is the underlying polymorphic link;\nentity_type='journal' means entity_id already IS the journal_id (a\nreceipt filed directly on a manual journal/bank deposit/transfer, see\napp/web/routes_banking.py). Every other entity_type (invoice/bill/\npayment/...) is resolved against Journal.source_type/source_id, the same\npolymorphic link Journal itself uses to point back at what created it.\njournal_id is null when nothing has posted yet (e.g. a receipt filed on\na draft bill) or entity_type doesn't correspond to a journal at all\n(contact/employee/purchase_order/staff_advance attachments).\n\nSigned URLs are valid 7 days (well past the default 300s -- these are\nmeant to be clicked from a refreshed dashboard, not used immediately) --\na caller that refreshes less often than that should treat url as\nneeding a fresh pull, not a permanent link.\n\nOne (attachment, journal) row per match, not one row per attachment --\nan entity_type='pay_run' attachment can fan out to more than one row\n(a pay run's main journal plus its seniority-accrual/advance-offset\njournals, see app/services/payroll.py, all sharing source_id=pay_run.id).",
    "operationId": "list_attachments_route_api_v1_external_attachments_get",
    "responses": {
     "200": {
      "description": "Successful Response",
      "content": {
       "application/json": {
        "schema": {}
       }
      }
     },
     "422": {
      "description": "Validation Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      }
     }
    },
    "tags": [
     "attachments"
    ]
   }
  },
  "/payroll": {
   "get": {
    "summary": "Payroll Report Route",
    "description": "Joe, 2026-09-29: \"a report for payroll in the system and an api\nendpoint for it that we can see the payroll for the period etc that I\ndon't think we can see now\" -- the payroll counterpart to\njournal-report, same reporting-scoped key, for a Power BI dashboard\nto pull actual payroll cost/tax/NSSF alongside the rest of the\nfinancials. Same \"posted pay runs only, period overlap\" rule as the\nin-app report (app/services/payroll_report.py).",
    "operationId": "payroll_report_route_api_v1_external_payroll_get",
    "parameters": [
     {
      "name": "date_from",
      "in": "query",
      "required": true,
      "schema": {
       "type": "string",
       "format": "date",
       "title": "Date From"
      }
     },
     {
      "name": "date_to",
      "in": "query",
      "required": true,
      "schema": {
       "type": "string",
       "format": "date",
       "title": "Date To"
      }
     },
     {
      "name": "run_type",
      "in": "query",
      "required": false,
      "schema": {
       "anyOf": [
        {
         "type": "string"
        },
        {
         "type": "null"
        }
       ],
       "title": "Run Type"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "Successful Response",
      "content": {
       "application/json": {
        "schema": {}
       }
      }
     },
     "422": {
      "description": "Validation Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      }
     }
    },
    "tags": [
     "payroll"
    ]
   }
  },
  "/tracking-categories": {
   "get": {
    "summary": "List Tracking Categories With Options",
    "description": "QBO's Class/Location equivalent -- up to 5 per org, each with its\nown options. One call returns everything needed to resolve a line's\ntrack_1_id..track_5_id, since a caller needs to know a dimension's\n`position` (which track_N_id column it fills) as much as its options.",
    "operationId": "list_tracking_categories_with_options_api_v1_external_tracking_categories_get",
    "responses": {
     "200": {
      "description": "Successful Response",
      "content": {
       "application/json": {
        "schema": {}
       }
      }
     },
     "422": {
      "description": "Validation Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      }
     }
    },
    "tags": [
     "tracking-categories"
    ]
   }
  },
  "/people": {
   "get": {
    "summary": "List People",
    "description": "Employees, plus contractor-flagged suppliers -- the two \"kinds\" of\nPerson Timesheets pulls (see Person.kind in the build brief).",
    "operationId": "list_people_api_v1_external_people_get",
    "responses": {
     "200": {
      "description": "Successful Response",
      "content": {
       "application/json": {
        "schema": {}
       }
      }
     },
     "422": {
      "description": "Validation Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      }
     }
    },
    "tags": [
     "people"
    ]
   }
  },
  "/public-holidays": {
   "get": {
    "summary": "List Public Holidays Route",
    "description": "This org's public holiday calendar (Settings > Leave settings) --\ngeneral-purpose, not leave-bot-specific (Timesheets pulls this to\nshade non-working days on its own calendar, see build_calendar/\npull_public_holidays in the Timesheets repo). Includes id so a\ncaller can dedupe/re-sync by holiday, not just by date.",
    "operationId": "list_public_holidays_route_api_v1_external_public_holidays_get",
    "responses": {
     "200": {
      "description": "Successful Response",
      "content": {
       "application/json": {
        "schema": {}
       }
      }
     },
     "422": {
      "description": "Validation Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      }
     }
    },
    "tags": [
     "public-holidays"
    ]
   }
  },
  "/contacts/{contact_id}/reparent": {
   "post": {
    "summary": "Reparent Contact",
    "description": "estimates-bot's /start flow: when a quote PDF's detected customer\nname differs from a project's currently-filed (UNMAPPED) parent, the\nbot offers to move it -- finds-or-creates a top-level contact by name\n(same matching POST /contacts/find-or-create uses) and reassigns this\ncontact's parent_contact_id to it. Found 2026-09-21 auditing every\nestimates-bot command against Ledger-live tenants: this was still\n100% hardcoded to QBO (find_or_create_customer_by_name +\nreassign_project_parent).",
    "operationId": "reparent_contact_api_v1_external_contacts__contact_id__reparent_post",
    "parameters": [
     {
      "name": "contact_id",
      "in": "path",
      "required": true,
      "schema": {
       "type": "string",
       "format": "uuid",
       "title": "Contact Id"
      }
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/ContactReparent"
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Successful Response",
      "content": {
       "application/json": {
        "schema": {}
       }
      }
     },
     "422": {
      "description": "Validation Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      }
     }
    },
    "tags": [
     "contacts"
    ]
   }
  },
  "/estimates": {
   "post": {
    "summary": "Create Estimate Route",
    "operationId": "create_estimate_route_api_v1_external_estimates_post",
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/EstimateCreate"
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Successful Response",
      "content": {
       "application/json": {
        "schema": {}
       }
      }
     },
     "422": {
      "description": "Validation Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      }
     }
    },
    "tags": [
     "estimates"
    ]
   },
   "get": {
    "summary": "List Estimates",
    "operationId": "list_estimates_api_v1_external_estimates_get",
    "parameters": [
     {
      "name": "contact_id",
      "in": "query",
      "required": false,
      "schema": {
       "anyOf": [
        {
         "type": "string",
         "format": "uuid"
        },
        {
         "type": "null"
        }
       ],
       "title": "Contact Id"
      }
     },
     {
      "name": "status",
      "in": "query",
      "required": false,
      "schema": {
       "anyOf": [
        {
         "type": "string"
        },
        {
         "type": "null"
        }
       ],
       "title": "Status"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "Successful Response",
      "content": {
       "application/json": {
        "schema": {}
       }
      }
     },
     "422": {
      "description": "Validation Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      }
     }
    },
    "tags": [
     "estimates"
    ]
   }
  },
  "/estimate-lines": {
   "get": {
    "summary": "Estimate Lines Report",
    "description": "One row per estimate LINE in a date range, for a Power BI dashboard --\nthe line-level shape the old QBO \"Estimate\" query had (one row per\nestimate line, header fields repeated on each). Joe, 2026-10-08: \"I need\n[it] for estimates too\". Accepts a Full key or a Reporting key (same as\nthe other dashboard lookups). Read-only.\n\ncustomer_id/customer_name are the estimate's own contact -- a project\n(sub-customer) when the quote was raised against one, with parent_name\nset to its company. converted_pct/remaining_pct show how much of the\nline is already invoiced (QBO had no such field). track_1..track_5 are\nthis org's tracking dimensions (GET /tracking-categories says which is\nwhich).",
    "operationId": "estimate_lines_report_api_v1_external_estimate_lines_get",
    "parameters": [
     {
      "name": "date_from",
      "in": "query",
      "required": true,
      "schema": {
       "type": "string",
       "format": "date",
       "title": "Date From"
      }
     },
     {
      "name": "date_to",
      "in": "query",
      "required": true,
      "schema": {
       "type": "string",
       "format": "date",
       "title": "Date To"
      }
     },
     {
      "name": "status",
      "in": "query",
      "required": false,
      "schema": {
       "anyOf": [
        {
         "type": "string"
        },
        {
         "type": "null"
        }
       ],
       "title": "Status"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "Successful Response",
      "content": {
       "application/json": {
        "schema": {}
       }
      }
     },
     "422": {
      "description": "Validation Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      }
     }
    },
    "tags": [
     "estimate-lines"
    ]
   }
  },
  "/documents": {
   "get": {
    "summary": "Documents Report",
    "description": "One row per invoice and bill (not draft, not void) dated in a range,\nwith what is still owed. Joe, 2026-10-09: the Power BI dashboard needs\nOpen Balance, Payment Date and Paid status that QBO put on each\ntransaction row. Join to journal-report on journal_id. outstanding is\nin the functional currency, the same basis as the AR/AP aging reports:\nthe control-account amount posted, less allocations, less manual\nsettlements. control_account_id is the AR (invoice) or AP (bill) account\nthe amount sits on, so the dashboard can attach the balance to that one\njournal line only. Accepts a Full key or a Reporting key. Read-only.",
    "operationId": "documents_report_api_v1_external_documents_get",
    "parameters": [
     {
      "name": "date_from",
      "in": "query",
      "required": true,
      "schema": {
       "type": "string",
       "format": "date",
       "title": "Date From"
      }
     },
     {
      "name": "date_to",
      "in": "query",
      "required": true,
      "schema": {
       "type": "string",
       "format": "date",
       "title": "Date To"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "Successful Response",
      "content": {
       "application/json": {
        "schema": {}
       }
      }
     },
     "422": {
      "description": "Validation Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      }
     }
    },
    "tags": [
     "documents"
    ]
   }
  },
  "/budget-lines": {
   "get": {
    "summary": "Budget Lines Report",
    "description": "One row per budget line whose accounting period overlaps a date\nrange, for a Power BI dashboard (Joe, 2026-10-09: \"budget needs to come\nfrom Ledger too\"). amount is the budgeted figure as entered (income and\nexpense both positive). signed_amount uses the same sign as the journal\nreport Amount column: debit positive, so income, liability and equity\nbudgets are negative. Only active budgets. version filters to\noriginal|revised|forecast. Accepts a Full key or a Reporting key.\nRead-only.",
    "operationId": "budget_lines_report_api_v1_external_budget_lines_get",
    "parameters": [
     {
      "name": "date_from",
      "in": "query",
      "required": true,
      "schema": {
       "type": "string",
       "format": "date",
       "title": "Date From"
      }
     },
     {
      "name": "date_to",
      "in": "query",
      "required": true,
      "schema": {
       "type": "string",
       "format": "date",
       "title": "Date To"
      }
     },
     {
      "name": "version",
      "in": "query",
      "required": false,
      "schema": {
       "anyOf": [
        {
         "type": "string"
        },
        {
         "type": "null"
        }
       ],
       "title": "Version"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "Successful Response",
      "content": {
       "application/json": {
        "schema": {}
       }
      }
     },
     "422": {
      "description": "Validation Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      }
     }
    },
    "tags": [
     "budget-lines"
    ]
   }
  },
  "/estimates/{estimate_id}": {
   "get": {
    "summary": "Get Estimate Route",
    "description": "Includes each line's remaining % -- the bot fleet's top-up flow\n(PARTIAL_BILLING_PLAN.md \u00a77b) needs this to show \"40% left\" and cap\nthe next conversion, which used to mean reading their own SharePoint\ntracker; here it's just this line's own converted_pct.",
    "operationId": "get_estimate_route_api_v1_external_estimates__estimate_id__get",
    "parameters": [
     {
      "name": "estimate_id",
      "in": "path",
      "required": true,
      "schema": {
       "type": "string",
       "format": "uuid",
       "title": "Estimate Id"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "Successful Response",
      "content": {
       "application/json": {
        "schema": {}
       }
      }
     },
     "422": {
      "description": "Validation Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      }
     }
    },
    "tags": [
     "estimates"
    ]
   }
  },
  "/estimates/{estimate_id}/convert": {
   "post": {
    "summary": "Convert Estimate Route",
    "operationId": "convert_estimate_route_api_v1_external_estimates__estimate_id__convert_post",
    "parameters": [
     {
      "name": "estimate_id",
      "in": "path",
      "required": true,
      "schema": {
       "type": "string",
       "format": "uuid",
       "title": "Estimate Id"
      }
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/EstimateConvert"
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Successful Response",
      "content": {
       "application/json": {
        "schema": {}
       }
      }
     },
     "422": {
      "description": "Validation Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      }
     }
    },
    "tags": [
     "estimates"
    ]
   }
  },
  "/invoices": {
   "get": {
    "summary": "List Invoices",
    "description": "Lets a caller resolve an invoice_no (what a human/bot actually has\nin hand) to a real id -- every other invoice route below takes an id,\nsame as GET /estimates already does for estimate_no. date_from/\ndate_to/stream_id/limit added 2026-09-16 for a bot-side weekly\nreconciliation (Myteb: comparing a week's sheet-logged invoices\nagainst what actually posted, per stream) -- the default 50-row cap\nstays for every existing caller that doesn't pass limit.",
    "operationId": "list_invoices_api_v1_external_invoices_get",
    "parameters": [
     {
      "name": "invoice_no",
      "in": "query",
      "required": false,
      "schema": {
       "anyOf": [
        {
         "type": "string"
        },
        {
         "type": "null"
        }
       ],
       "title": "Invoice No"
      }
     },
     {
      "name": "contact_id",
      "in": "query",
      "required": false,
      "schema": {
       "anyOf": [
        {
         "type": "string",
         "format": "uuid"
        },
        {
         "type": "null"
        }
       ],
       "title": "Contact Id"
      }
     },
     {
      "name": "status",
      "in": "query",
      "required": false,
      "schema": {
       "anyOf": [
        {
         "type": "string"
        },
        {
         "type": "null"
        }
       ],
       "title": "Status"
      }
     },
     {
      "name": "stream_id",
      "in": "query",
      "required": false,
      "schema": {
       "anyOf": [
        {
         "type": "string",
         "format": "uuid"
        },
        {
         "type": "null"
        }
       ],
       "title": "Stream Id"
      }
     },
     {
      "name": "date_from",
      "in": "query",
      "required": false,
      "schema": {
       "anyOf": [
        {
         "type": "string",
         "format": "date"
        },
        {
         "type": "null"
        }
       ],
       "title": "Date From"
      }
     },
     {
      "name": "date_to",
      "in": "query",
      "required": false,
      "schema": {
       "anyOf": [
        {
         "type": "string",
         "format": "date"
        },
        {
         "type": "null"
        }
       ],
       "title": "Date To"
      }
     },
     {
      "name": "limit",
      "in": "query",
      "required": false,
      "schema": {
       "type": "integer",
       "default": 50,
       "title": "Limit"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "Successful Response",
      "content": {
       "application/json": {
        "schema": {}
       }
      }
     },
     "422": {
      "description": "Validation Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      }
     }
    },
    "tags": [
     "invoices"
    ]
   },
   "post": {
    "summary": "Create And Post Invoice",
    "operationId": "create_and_post_invoice_api_v1_external_invoices_post",
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/InvoiceCreate"
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Successful Response",
      "content": {
       "application/json": {
        "schema": {}
       }
      }
     },
     "422": {
      "description": "Validation Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      }
     }
    },
    "tags": [
     "invoices"
    ]
   }
  },
  "/invoices/{invoice_id}/status": {
   "get": {
    "summary": "Invoice Status",
    "description": "Joe, 2026-09-21: estimates-bot's /checkinvoicestatus (customer,\ndate, amount, open balance, or -- once fully paid -- which payment(s)\nsettled it and to which bank account) has no equivalent on plain\nGET /invoices, which only ever returns the brief shape every other\ncaller already depends on. Kept as its own endpoint rather than\nwidening that one.",
    "operationId": "invoice_status_api_v1_external_invoices__invoice_id__status_get",
    "parameters": [
     {
      "name": "invoice_id",
      "in": "path",
      "required": true,
      "schema": {
       "type": "string",
       "format": "uuid",
       "title": "Invoice Id"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "Successful Response",
      "content": {
       "application/json": {
        "schema": {}
       }
      }
     },
     "422": {
      "description": "Validation Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      }
     }
    },
    "tags": [
     "invoices"
    ]
   }
  },
  "/invoices/{invoice_id}/detail": {
   "get": {
    "summary": "Get Invoice Detail",
    "description": "Full read-only view -- line items plus the same status/payment info\nGET /invoices/{id}/status already returns, so a caller that wants to\nactually SHOW an invoice (not just check whether it's settled) doesn't\nneed a second round trip. Kept as its own endpoint rather than widening\n/status, same reasoning as that route's own docstring. Joe, 2026-09-26:\nthe Mini App's own \"view invoices already done\" ask -- doesn't need to\nedit, just see what's on it.",
    "operationId": "get_invoice_detail_api_v1_external_invoices__invoice_id__detail_get",
    "parameters": [
     {
      "name": "invoice_id",
      "in": "path",
      "required": true,
      "schema": {
       "type": "string",
       "format": "uuid",
       "title": "Invoice Id"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "Successful Response",
      "content": {
       "application/json": {
        "schema": {}
       }
      }
     },
     "422": {
      "description": "Validation Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      }
     }
    },
    "tags": [
     "invoices"
    ]
   }
  },
  "/bills": {
   "get": {
    "summary": "List Bills",
    "description": "their_ref (2026-10-06): the supplier's own invoice number. A bot that\nis about to post a supplier bill uses it with contact_id to check if the\nbill is already in the Ledger. The match ignores case and spaces at the\nends. Void bills are not excluded here: pass status to filter.\n\nJoe, 2026-09-30: \"need to be able to see the cash advances and the\nexpenses assigned too, not just add expense\" -- the Mini App could\ncreate a bill or an already-paid expense (POST .../bills,\nPOST .../expenses -- both write a real Bill row, see\ncreate_bill_payment's own docstring) but had no way to browse what\nhad actually been recorded. Same shape as GET /invoices.",
    "operationId": "list_bills_api_v1_external_bills_get",
    "parameters": [
     {
      "name": "bill_no",
      "in": "query",
      "required": false,
      "schema": {
       "anyOf": [
        {
         "type": "string"
        },
        {
         "type": "null"
        }
       ],
       "title": "Bill No"
      }
     },
     {
      "name": "contact_id",
      "in": "query",
      "required": false,
      "schema": {
       "anyOf": [
        {
         "type": "string",
         "format": "uuid"
        },
        {
         "type": "null"
        }
       ],
       "title": "Contact Id"
      }
     },
     {
      "name": "status",
      "in": "query",
      "required": false,
      "schema": {
       "anyOf": [
        {
         "type": "string"
        },
        {
         "type": "null"
        }
       ],
       "title": "Status"
      }
     },
     {
      "name": "date_from",
      "in": "query",
      "required": false,
      "schema": {
       "anyOf": [
        {
         "type": "string",
         "format": "date"
        },
        {
         "type": "null"
        }
       ],
       "title": "Date From"
      }
     },
     {
      "name": "date_to",
      "in": "query",
      "required": false,
      "schema": {
       "anyOf": [
        {
         "type": "string",
         "format": "date"
        },
        {
         "type": "null"
        }
       ],
       "title": "Date To"
      }
     },
     {
      "name": "limit",
      "in": "query",
      "required": false,
      "schema": {
       "type": "integer",
       "default": 50,
       "title": "Limit"
      }
     },
     {
      "name": "their_ref",
      "in": "query",
      "required": false,
      "schema": {
       "anyOf": [
        {
         "type": "string"
        },
        {
         "type": "null"
        }
       ],
       "title": "Their Ref"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "Successful Response",
      "content": {
       "application/json": {
        "schema": {}
       }
      }
     },
     "422": {
      "description": "Validation Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      }
     }
    },
    "tags": [
     "bills"
    ]
   },
   "post": {
    "summary": "Create And Post Bill",
    "description": "A genuine payable, still outstanding -- the /bill-payments endpoint\nabove is for an expense already paid at the moment it's recorded\n(creates + posts + immediately pays). This is that same create+post\nhalf on its own, for a bill the bot records now but that won't be\npaid until later (matches the web UI's own Bills screen, not its\nExpense screen).",
    "operationId": "create_and_post_bill_api_v1_external_bills_post",
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/BillCreate"
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Successful Response",
      "content": {
       "application/json": {
        "schema": {}
       }
      }
     },
     "422": {
      "description": "Validation Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      }
     }
    },
    "tags": [
     "bills"
    ]
   }
  },
  "/bills/{bill_id}/detail": {
   "get": {
    "summary": "Get Bill Detail",
    "description": "Read-only view -- line items plus payment info, same shape and\nsame \"just the view, no editing\" scope as GET /invoices/{id}/detail.",
    "operationId": "get_bill_detail_api_v1_external_bills__bill_id__detail_get",
    "parameters": [
     {
      "name": "bill_id",
      "in": "path",
      "required": true,
      "schema": {
       "type": "string",
       "format": "uuid",
       "title": "Bill Id"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "Successful Response",
      "content": {
       "application/json": {
        "schema": {}
       }
      }
     },
     "422": {
      "description": "Validation Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      }
     }
    },
    "tags": [
     "bills"
    ]
   }
  },
  "/tax/calctax-workbook": {
   "get": {
    "summary": "Calctax Workbook",
    "description": "estimates-bot's /calctax (Joe, 2026-09-21: \"calctax should leverage\nthe tax module we have now in ledger, but just use the local LLM for\nthe analysis like it does now\"). Generates this org's WHT, e-commerce\nVAT (reverse charge), Salary Tax (TOS) and Fringe Benefit Tax draft\nreturns for the given calendar month -- the same generate_* calls\nSettings > Tax Returns' own \"Generate\" button makes, so every /calctax\nrun leaves a real draft TaxReturn behind, same as a human clicking it\n-- plus the Untagged Bills completeness sheet (see\napp.services.tax.UntaggedBillLine), and renders all of it into one\nworkbook for estimates-bot to hand to its own local-LLM review pass.\nNo registration-flag gating (unlike generate_selected_tax_returns) --\n/calctax has always computed WHT/Ecomm/Salary unconditionally,\nregardless of whether an org happens to be registered for WHT.",
    "operationId": "calctax_workbook_api_v1_external_tax_calctax_workbook_get",
    "parameters": [
     {
      "name": "year",
      "in": "query",
      "required": true,
      "schema": {
       "type": "integer",
       "title": "Year"
      }
     },
     {
      "name": "month",
      "in": "query",
      "required": true,
      "schema": {
       "type": "integer",
       "title": "Month"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "Successful Response",
      "content": {
       "application/json": {
        "schema": {}
       }
      }
     },
     "422": {
      "description": "Validation Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      }
     }
    },
    "tags": [
     "tax"
    ]
   }
  },
  "/invoices/{invoice_id}/pdf": {
   "get": {
    "summary": "Get Invoice Pdf Route",
    "description": "Raw PDF bytes for a satellite app's own flow to save/forward itself\n(SEVEA's Power Automate flow: POST /invoices then this, instead of\ngoing through send-email/send-telegram) -- same rendering as those\ntwo routes, just handed back directly instead of being emailed/sent.\n\n`template_id` (added 2026-09-21, estimates-bot's /pdf reprint template\npicker -- Joe: \"when reprinting it should give the option to change\nthe template\") renders with a different template than the one\nactually assigned to the invoice/stream, without persisting anything\n-- e.g. re-requesting the Branded layout on an invoice originally\nsent Unbranded. Must belong to the same org as the invoice, same as\nthe invoice lookup itself.",
    "operationId": "get_invoice_pdf_route_api_v1_external_invoices__invoice_id__pdf_get",
    "parameters": [
     {
      "name": "invoice_id",
      "in": "path",
      "required": true,
      "schema": {
       "type": "string",
       "format": "uuid",
       "title": "Invoice Id"
      }
     },
     {
      "name": "template_id",
      "in": "query",
      "required": false,
      "schema": {
       "anyOf": [
        {
         "type": "string",
         "format": "uuid"
        },
        {
         "type": "null"
        }
       ],
       "title": "Template Id"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "Successful Response",
      "content": {
       "application/json": {
        "schema": {}
       }
      }
     },
     "422": {
      "description": "Validation Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      }
     }
    },
    "tags": [
     "invoices"
    ]
   }
  },
  "/invoices/{invoice_id}/pay-link": {
   "get": {
    "summary": "Get Invoice Pay Link Route",
    "description": "The public pay link for an invoice, for a satellite bot to post next to\nthe PDF it fetched from /invoices/{id}/pdf (EGE's stock bot puts both in\nits Stock Admin group). `url` is null when no payment gateway applies to\nthis invoice's stream -- the bot should then post the PDF alone rather\nthan a link that only shows bank-transfer details. `payable` is false for\na draft, voided or fully paid invoice.",
    "operationId": "get_invoice_pay_link_route_api_v1_external_invoices__invoice_id__pay_link_get",
    "parameters": [
     {
      "name": "invoice_id",
      "in": "path",
      "required": true,
      "schema": {
       "type": "string",
       "format": "uuid",
       "title": "Invoice Id"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "Successful Response",
      "content": {
       "application/json": {
        "schema": {}
       }
      }
     },
     "422": {
      "description": "Validation Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      }
     }
    },
    "tags": [
     "invoices"
    ]
   }
  },
  "/invoices/{invoice_id}/attachments": {
   "post": {
    "summary": "Attach To Invoice",
    "operationId": "attach_to_invoice_api_v1_external_invoices__invoice_id__attachments_post",
    "parameters": [
     {
      "name": "invoice_id",
      "in": "path",
      "required": true,
      "schema": {
       "type": "string",
       "format": "uuid",
       "title": "Invoice Id"
      }
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/AttachmentCreate"
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Successful Response",
      "content": {
       "application/json": {
        "schema": {}
       }
      }
     },
     "422": {
      "description": "Validation Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      }
     }
    },
    "tags": [
     "invoices"
    ]
   }
  },
  "/bill-payments": {
   "get": {
    "summary": "List Bill Payments",
    "operationId": "list_bill_payments_api_v1_external_bill_payments_get",
    "parameters": [
     {
      "name": "voucher_prefix",
      "in": "query",
      "required": false,
      "schema": {
       "anyOf": [
        {
         "type": "string"
        },
        {
         "type": "null"
        }
       ],
       "title": "Voucher Prefix"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "Successful Response",
      "content": {
       "application/json": {
        "schema": {}
       }
      }
     },
     "422": {
      "description": "Validation Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      }
     }
    },
    "tags": [
     "bill-payments"
    ]
   },
   "post": {
    "summary": "Create Bill Payment",
    "description": "An expense/bill-payment in one call: creates the bill, posts it,\nthen pays it in full immediately from the given bank account -- the\nsame \"already-paid bill\" shape the web UI's own Expense screen uses\n(see routes_banking.py). voucher_no is stored as-is on the resulting\nPayment; the caller (not this system) is responsible for it being\ncorrect and not already in use.\n\nTax lives on the BILL, not the payment (per Joe) -- each line's\ntax_rate_id/wht_rate_id go through _resolve_bill_line_inputs, the same\nresolver the web Bill/Expense forms use, so a reverse-charge VAT rate's\nbundled WHT is always derived server-side and never trusted from the\ncaller, exactly as for a human-entered bill.\n\nadvance_id links the resulting payment's journal to a specific cash\nadvance (see routes_banking.py's own expense_create) -- required\nwhenever bank_account_id is that advance's own holder account, since\nget_advance_balance/get_advance_transactions only ever see journals\nexplicitly linked this way, not just any activity on the account\n(which is shared across every advance the same holder has ever had).\nConfirmed missing live during bot testing 2026-09-08: a biz-adv\nexpense posted and paid correctly but the advance's own balance\nnever moved without this.",
    "operationId": "create_bill_payment_api_v1_external_bill_payments_post",
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/BillPaymentCreate"
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Successful Response",
      "content": {
       "application/json": {
        "schema": {}
       }
      }
     },
     "422": {
      "description": "Validation Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      }
     }
    },
    "tags": [
     "bill-payments"
    ]
   }
  },
  "/bills/{bill_id}/attachments": {
   "post": {
    "summary": "Attach To Bill",
    "description": "Same shape as attach_to_invoice above -- Joe: attachments on bills\nand expenses are essential to how the business runs, so the bot fleet's\nreceipt-photo-in, bill-created-out flow needs this exactly as much as\ninvoices do.",
    "operationId": "attach_to_bill_api_v1_external_bills__bill_id__attachments_post",
    "parameters": [
     {
      "name": "bill_id",
      "in": "path",
      "required": true,
      "schema": {
       "type": "string",
       "format": "uuid",
       "title": "Bill Id"
      }
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/AttachmentCreate"
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Successful Response",
      "content": {
       "application/json": {
        "schema": {}
       }
      }
     },
     "422": {
      "description": "Validation Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      }
     }
    },
    "tags": [
     "bills"
    ]
   }
  },
  "/contacts/{contact_id}/attachments": {
   "post": {
    "summary": "Attach To Contact",
    "description": "Same shape as attach_to_bill -- Joe: \"keep the patent attached to\nthe client too\". The web Create Contact form links the certificate it\nextracted from (see routes_arap.contact_create); this is the JSON\nequivalent for the Mini App's quick-add, called right after\nfind-or-create returns the new contact's id. A byte-identical file\nalready uploaded (e.g. the same certificate extracted twice) is reused\nrather than 409'd, same as the web form's own extraction upload.",
    "operationId": "attach_to_contact_api_v1_external_contacts__contact_id__attachments_post",
    "parameters": [
     {
      "name": "contact_id",
      "in": "path",
      "required": true,
      "schema": {
       "type": "string",
       "format": "uuid",
       "title": "Contact Id"
      }
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/AttachmentCreate"
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Successful Response",
      "content": {
       "application/json": {
        "schema": {}
       }
      }
     },
     "422": {
      "description": "Validation Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      }
     }
    },
    "tags": [
     "contacts"
    ]
   }
  },
  "/bills/{bill_id}/their-ref": {
   "patch": {
    "summary": "Update Bill Their Ref",
    "description": "Set the supplier's invoice number on a bill (nothing else changes).\nThe stock bot uses it when the supplier invoice arrives after the stock\nwas received and the bot already posted the bill.",
    "operationId": "update_bill_their_ref_api_v1_external_bills__bill_id__their_ref_patch",
    "parameters": [
     {
      "name": "bill_id",
      "in": "path",
      "required": true,
      "schema": {
       "type": "string",
       "format": "uuid",
       "title": "Bill Id"
      }
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/BillTheirRefUpdate"
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Successful Response",
      "content": {
       "application/json": {
        "schema": {}
       }
      }
     },
     "422": {
      "description": "Validation Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      }
     }
    },
    "tags": [
     "bills"
    ]
   }
  },
  "/ar-aging": {
   "get": {
    "summary": "Get Ar Aging",
    "operationId": "get_ar_aging_api_v1_external_ar_aging_get",
    "parameters": [
     {
      "name": "contact_id",
      "in": "query",
      "required": false,
      "schema": {
       "anyOf": [
        {
         "type": "string",
         "format": "uuid"
        },
        {
         "type": "null"
        }
       ],
       "title": "Contact Id"
      }
     },
     {
      "name": "as_of",
      "in": "query",
      "required": false,
      "schema": {
       "anyOf": [
        {
         "type": "string",
         "format": "date"
        },
        {
         "type": "null"
        }
       ],
       "title": "As Of"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "Successful Response",
      "content": {
       "application/json": {
        "schema": {}
       }
      }
     },
     "422": {
      "description": "Validation Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      }
     }
    },
    "tags": [
     "ar-aging"
    ]
   }
  },
  "/ap-aging": {
   "get": {
    "summary": "Get Ap Aging",
    "operationId": "get_ap_aging_api_v1_external_ap_aging_get",
    "parameters": [
     {
      "name": "contact_id",
      "in": "query",
      "required": false,
      "schema": {
       "anyOf": [
        {
         "type": "string",
         "format": "uuid"
        },
        {
         "type": "null"
        }
       ],
       "title": "Contact Id"
      }
     },
     {
      "name": "as_of",
      "in": "query",
      "required": false,
      "schema": {
       "anyOf": [
        {
         "type": "string",
         "format": "date"
        },
        {
         "type": "null"
        }
       ],
       "title": "As Of"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "Successful Response",
      "content": {
       "application/json": {
        "schema": {}
       }
      }
     },
     "422": {
      "description": "Validation Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      }
     }
    },
    "tags": [
     "ap-aging"
    ]
   }
  },
  "/contacts/{contact_id}/open-documents": {
   "get": {
    "summary": "Get Contact Open Documents",
    "description": "Every still-open invoice/bill for one contact in one currency, with\nhow much -- the same source the web UI's Receive Payment/Pay Bills\nmulti-document pickers use (list_open_documents), useful for a bot\nthat needs to show \"which of this customer's invoices is this\npayment for\" rather than a whole-org aging list.",
    "operationId": "get_contact_open_documents_api_v1_external_contacts__contact_id__open_documents_get",
    "parameters": [
     {
      "name": "contact_id",
      "in": "path",
      "required": true,
      "schema": {
       "type": "string",
       "format": "uuid",
       "title": "Contact Id"
      }
     },
     {
      "name": "document_type",
      "in": "query",
      "required": true,
      "schema": {
       "type": "string",
       "title": "Document Type"
      }
     },
     {
      "name": "currency",
      "in": "query",
      "required": false,
      "schema": {
       "type": "string",
       "default": "USD",
       "title": "Currency"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "Successful Response",
      "content": {
       "application/json": {
        "schema": {}
       }
      }
     },
     "422": {
      "description": "Validation Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      }
     }
    },
    "tags": [
     "contacts"
    ]
   }
  },
  "/contacts/{contact_id}/statement": {
   "get": {
    "summary": "Get Contact Statement",
    "description": "Every charge (invoice/bill raised) and every real payment or non-\ncash settlement against this contact, in date order with a running\nbalance -- see contact_statement's own docstring for why this is\nbuilt at the document level rather than off raw journal activity\n(a settled-but-never-paid document still needs to show as cleared).\ndate_from/date_to only trim which rows come back; the running\nbalance on the first returned row is still the true opening balance\n(computed from the contact's full history, not reset to 0 at the\nwindow edge).",
    "operationId": "get_contact_statement_api_v1_external_contacts__contact_id__statement_get",
    "parameters": [
     {
      "name": "contact_id",
      "in": "path",
      "required": true,
      "schema": {
       "type": "string",
       "format": "uuid",
       "title": "Contact Id"
      }
     },
     {
      "name": "document_type",
      "in": "query",
      "required": true,
      "schema": {
       "type": "string",
       "title": "Document Type"
      }
     },
     {
      "name": "date_from",
      "in": "query",
      "required": false,
      "schema": {
       "anyOf": [
        {
         "type": "string",
         "format": "date"
        },
        {
         "type": "null"
        }
       ],
       "title": "Date From"
      }
     },
     {
      "name": "date_to",
      "in": "query",
      "required": false,
      "schema": {
       "anyOf": [
        {
         "type": "string",
         "format": "date"
        },
        {
         "type": "null"
        }
       ],
       "title": "Date To"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "Successful Response",
      "content": {
       "application/json": {
        "schema": {}
       }
      }
     },
     "422": {
      "description": "Validation Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      }
     }
    },
    "tags": [
     "contacts"
    ]
   }
  },
  "/receipts": {
   "post": {
    "summary": "Create Receipt",
    "description": "The AR counterpart to /bill-payments: allocates real cash across\none or more of a customer's ALREADY-OPEN invoices (see GET\n/contacts/{id}/open-documents for what to pass here), the same\nmulti-document shape as the web UI's Receive Payment screen -- unlike\n/bill-payments, this never creates a new document itself.",
    "operationId": "create_receipt_api_v1_external_receipts_post",
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/ReceiptCreate"
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Successful Response",
      "content": {
       "application/json": {
        "schema": {}
       }
      }
     },
     "422": {
      "description": "Validation Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      }
     }
    },
    "tags": [
     "receipts"
    ]
   }
  },
  "/open-invoices": {
   "get": {
    "summary": "Search Open Invoices Route",
    "description": "Open invoices (still owing, in one currency) whose number, customer\nname or project name contains `q` -- see search_open_invoices. Each row\ncarries the invoice's own contact_id so a caller can start a receipt\nfor it; project_name is set only when the invoice is on a project\n(then customer_name is that project's parent customer).",
    "operationId": "search_open_invoices_route_api_v1_external_open_invoices_get",
    "parameters": [
     {
      "name": "q",
      "in": "query",
      "required": true,
      "schema": {
       "type": "string",
       "title": "Q"
      }
     },
     {
      "name": "currency",
      "in": "query",
      "required": false,
      "schema": {
       "type": "string",
       "default": "USD",
       "title": "Currency"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "Successful Response",
      "content": {
       "application/json": {
        "schema": {}
       }
      }
     },
     "422": {
      "description": "Validation Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      }
     }
    },
    "tags": [
     "open-invoices"
    ]
   }
  },
  "/bank-fees": {
   "post": {
    "summary": "Create Bank Fee",
    "description": "Built for bankrecbot's ABA email listener -- a bank-charged fee\n(a transfer fee, a maintenance fee, ...) arrives as its own\nstandalone debit advice, never tied to an existing invoice/bill, so\nthis posts a plain two-line journal (Dr the expense account / Cr the\nbank account) rather than going through invoice/bill payment\nmatching. Journal has no dedicated reference column, so idempotency\nis checked against `reference` embedded in the narration instead --\na second call with the same reference returns the already-posted\njournal rather than creating a duplicate.",
    "operationId": "create_bank_fee_api_v1_external_bank_fees_post",
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/BankFeeCreate"
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Successful Response",
      "content": {
       "application/json": {
        "schema": {}
       }
      }
     },
     "422": {
      "description": "Validation Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      }
     }
    },
    "tags": [
     "bank-fees"
    ]
   }
  },
  "/transfers": {
   "post": {
    "summary": "Create Transfer Route",
    "description": "Built for bankrecbot's ABA email listener -- money moving between\ntwo of a client's OWN accounts (detected on the bank's side by both\naccount numbers belonging to the same client), not a customer/supplier\ndocument.\n\nA single real transfer generates TWO separate ABA notifications (a\ndebit advice on the sending account, a credit advice on the\nreceiving account) -- bankrecbot's listener can call this from\neither side, and ABA doesn't guarantee they share the same\n\"Reference #\", so idempotency here is NOT keyed on `reference` alone.\nInstead it looks for an existing bank_transfer journal on the SAME\ndate whose destination leg (a debit of the full amount to\nto_account_id -- see create_bank_transfer's own line shape) already\nmatches -- the same real-world movement always produces that exact\nleg regardless of which side reported it first.",
    "operationId": "create_transfer_route_api_v1_external_transfers_post",
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/TransferCreate"
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Successful Response",
      "content": {
       "application/json": {
        "schema": {}
       }
      }
     },
     "422": {
      "description": "Validation Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      }
     }
    },
    "tags": [
     "transfers"
    ]
   }
  },
  "/bill-settlements": {
   "post": {
    "summary": "Create Bill Settlement",
    "description": "The AP counterpart to /receipts -- pays real cash against one or\nmore of a vendor's ALREADY-OPEN bills (see GET /ap-aging or\n/contacts/{id}/open-documents?document_type=bill for what to pass\nhere). Distinct from POST /bill-payments, which always creates a\nbrand-new bill first -- this is for a supplier payment that arrives\nmatched to an EXISTING bill (e.g. an invoice number lifted from a\nbank transfer's remark), the same way /receipts never creates a new\ninvoice for an incoming payment.",
    "operationId": "create_bill_settlement_api_v1_external_bill_settlements_post",
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/BillSettlementCreate"
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Successful Response",
      "content": {
       "application/json": {
        "schema": {}
       }
      }
     },
     "422": {
      "description": "Validation Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      }
     }
    },
    "tags": [
     "bill-settlements"
    ]
   }
  },
  "/cash-advances": {
   "post": {
    "summary": "Create Cash Advance",
    "operationId": "create_cash_advance_api_v1_external_cash_advances_post",
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/CashAdvanceCreate"
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Successful Response",
      "content": {
       "application/json": {
        "schema": {}
       }
      }
     },
     "422": {
      "description": "Validation Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      }
     }
    },
    "tags": [
     "cash-advances"
    ]
   },
   "get": {
    "summary": "Get Cash Advances",
    "description": "account_id -- which specific advance a funding account belongs to\n(the same account can be shared by several of one holder's advances\nover time, open and settled) -- lets a caller that only knows the\naccount (e.g. a bot's Telegram-picker session, see ledger_poster.py)\nfind the right advance_id to link an expense to, without the bot\nneeding to track advance_id as its own separate piece of state.",
    "operationId": "get_cash_advances_api_v1_external_cash_advances_get",
    "parameters": [
     {
      "name": "status",
      "in": "query",
      "required": false,
      "schema": {
       "anyOf": [
        {
         "type": "string"
        },
        {
         "type": "null"
        }
       ],
       "title": "Status"
      }
     },
     {
      "name": "account_id",
      "in": "query",
      "required": false,
      "schema": {
       "anyOf": [
        {
         "type": "string",
         "format": "uuid"
        },
        {
         "type": "null"
        }
       ],
       "title": "Account Id"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "Successful Response",
      "content": {
       "application/json": {
        "schema": {}
       }
      }
     },
     "422": {
      "description": "Validation Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      }
     }
    },
    "tags": [
     "cash-advances"
    ]
   }
  },
  "/cash-advances/{advance_id}": {
   "get": {
    "summary": "Get Cash Advance",
    "operationId": "get_cash_advance_api_v1_external_cash_advances__advance_id__get",
    "parameters": [
     {
      "name": "advance_id",
      "in": "path",
      "required": true,
      "schema": {
       "type": "string",
       "format": "uuid",
       "title": "Advance Id"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "Successful Response",
      "content": {
       "application/json": {
        "schema": {}
       }
      }
     },
     "422": {
      "description": "Validation Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      }
     }
    },
    "tags": [
     "cash-advances"
    ]
   }
  },
  "/cash-advances/{advance_id}/fund": {
   "post": {
    "summary": "Fund Cash Advance",
    "operationId": "fund_cash_advance_api_v1_external_cash_advances__advance_id__fund_post",
    "parameters": [
     {
      "name": "advance_id",
      "in": "path",
      "required": true,
      "schema": {
       "type": "string",
       "format": "uuid",
       "title": "Advance Id"
      }
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/CashAdvanceFund"
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Successful Response",
      "content": {
       "application/json": {
        "schema": {}
       }
      }
     },
     "422": {
      "description": "Validation Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      }
     }
    },
    "tags": [
     "cash-advances"
    ]
   }
  },
  "/cash-advances/{advance_id}/settle": {
   "post": {
    "summary": "Settle Cash Advance",
    "description": "Whatever's left moves in ONE transfer, in whichever direction the\nbalance calls for (see settle_advance's own docstring) -- a leftover\nasset-side balance returns to the bank, a negative balance (holder\nspent more than given) is reimbursed from it. Either way this closes\nthe advance.",
    "operationId": "settle_cash_advance_api_v1_external_cash_advances__advance_id__settle_post",
    "parameters": [
     {
      "name": "advance_id",
      "in": "path",
      "required": true,
      "schema": {
       "type": "string",
       "format": "uuid",
       "title": "Advance Id"
      }
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/CashAdvanceSettle"
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Successful Response",
      "content": {
       "application/json": {
        "schema": {}
       }
      }
     },
     "422": {
      "description": "Validation Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      }
     }
    },
    "tags": [
     "cash-advances"
    ]
   }
  },
  "/invoices/{invoice_id}/void": {
   "post": {
    "summary": "Void Invoice Route",
    "operationId": "void_invoice_route_api_v1_external_invoices__invoice_id__void_post",
    "parameters": [
     {
      "name": "invoice_id",
      "in": "path",
      "required": true,
      "schema": {
       "type": "string",
       "format": "uuid",
       "title": "Invoice Id"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "Successful Response",
      "content": {
       "application/json": {
        "schema": {}
       }
      }
     },
     "422": {
      "description": "Validation Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      }
     }
    },
    "tags": [
     "invoices"
    ]
   }
  },
  "/invoices/{invoice_id}/archive": {
   "post": {
    "summary": "Archive Invoice Route",
    "description": "For a genuinely erroneous/duplicate document -- a bad automation\nretry, not a real cancelled sale (that's still /void) -- voids it,\nhides it, and gives its number back for reuse. Only ever safe for\nthe single most-recently-issued number on its stream; anything else\nraises 409, same reasoning as SEVEA's own Power Automate flow\nretrying into a duplicate invoice.",
    "operationId": "archive_invoice_route_api_v1_external_invoices__invoice_id__archive_post",
    "parameters": [
     {
      "name": "invoice_id",
      "in": "path",
      "required": true,
      "schema": {
       "type": "string",
       "format": "uuid",
       "title": "Invoice Id"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "Successful Response",
      "content": {
       "application/json": {
        "schema": {}
       }
      }
     },
     "422": {
      "description": "Validation Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      }
     }
    },
    "tags": [
     "invoices"
    ]
   }
  },
  "/invoices/{invoice_id}/reissue": {
   "post": {
    "summary": "Reissue Invoice Route",
    "operationId": "reissue_invoice_route_api_v1_external_invoices__invoice_id__reissue_post",
    "parameters": [
     {
      "name": "invoice_id",
      "in": "path",
      "required": true,
      "schema": {
       "type": "string",
       "format": "uuid",
       "title": "Invoice Id"
      }
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/InvoiceReissue"
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Successful Response",
      "content": {
       "application/json": {
        "schema": {}
       }
      }
     },
     "422": {
      "description": "Validation Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      }
     }
    },
    "tags": [
     "invoices"
    ]
   }
  },
  "/credit-notes": {
   "get": {
    "summary": "List Credit Notes",
    "description": "For a bot-side weekly reconciliation (Myteb) reporting credit notes\nissued in a window -- these reduce revenue but aren't tracked in the\ninvoice-logging sheet the reconciliation compares against, so they're\nsurfaced separately.",
    "operationId": "list_credit_notes_api_v1_external_credit_notes_get",
    "parameters": [
     {
      "name": "date_from",
      "in": "query",
      "required": false,
      "schema": {
       "anyOf": [
        {
         "type": "string",
         "format": "date"
        },
        {
         "type": "null"
        }
       ],
       "title": "Date From"
      }
     },
     {
      "name": "date_to",
      "in": "query",
      "required": false,
      "schema": {
       "anyOf": [
        {
         "type": "string",
         "format": "date"
        },
        {
         "type": "null"
        }
       ],
       "title": "Date To"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "Successful Response",
      "content": {
       "application/json": {
        "schema": {}
       }
      }
     },
     "422": {
      "description": "Validation Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      }
     }
    },
    "tags": [
     "credit-notes"
    ]
   },
   "post": {
    "summary": "Create Credit Note Route",
    "operationId": "create_credit_note_route_api_v1_external_credit_notes_post",
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/CreditNoteCreate"
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Successful Response",
      "content": {
       "application/json": {
        "schema": {}
       }
      }
     },
     "422": {
      "description": "Validation Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      }
     }
    },
    "tags": [
     "credit-notes"
    ]
   }
  },
  "/credit-notes/{credit_note_id}/post": {
   "post": {
    "summary": "Post Credit Note Route",
    "operationId": "post_credit_note_route_api_v1_external_credit_notes__credit_note_id__post_post",
    "parameters": [
     {
      "name": "credit_note_id",
      "in": "path",
      "required": true,
      "schema": {
       "type": "string",
       "format": "uuid",
       "title": "Credit Note Id"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "Successful Response",
      "content": {
       "application/json": {
        "schema": {}
       }
      }
     },
     "422": {
      "description": "Validation Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      }
     }
    },
    "tags": [
     "credit-notes"
    ]
   }
  },
  "/credit-notes/{credit_note_id}/apply": {
   "post": {
    "summary": "Apply Credit Note Route",
    "operationId": "apply_credit_note_route_api_v1_external_credit_notes__credit_note_id__apply_post",
    "parameters": [
     {
      "name": "credit_note_id",
      "in": "path",
      "required": true,
      "schema": {
       "type": "string",
       "format": "uuid",
       "title": "Credit Note Id"
      }
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/CreditNoteApply"
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Successful Response",
      "content": {
       "application/json": {
        "schema": {}
       }
      }
     },
     "422": {
      "description": "Validation Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      }
     }
    },
    "tags": [
     "credit-notes"
    ]
   }
  },
  "/credit-notes/{credit_note_id}/void": {
   "post": {
    "summary": "Void Credit Note Route",
    "operationId": "void_credit_note_route_api_v1_external_credit_notes__credit_note_id__void_post",
    "parameters": [
     {
      "name": "credit_note_id",
      "in": "path",
      "required": true,
      "schema": {
       "type": "string",
       "format": "uuid",
       "title": "Credit Note Id"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "Successful Response",
      "content": {
       "application/json": {
        "schema": {}
       }
      }
     },
     "422": {
      "description": "Validation Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      }
     }
    },
    "tags": [
     "credit-notes"
    ]
   }
  },
  "/journals": {
   "post": {
    "summary": "Create Manual Journal",
    "operationId": "create_manual_journal_api_v1_external_journals_post",
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/ManualJournalCreate"
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Successful Response",
      "content": {
       "application/json": {
        "schema": {}
       }
      }
     },
     "422": {
      "description": "Validation Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      }
     }
    },
    "tags": [
     "journals"
    ]
   }
  },
  "/stock-items": {
   "post": {
    "summary": "Ensure Stock Item Route",
    "description": "Find-or-create by code. A second call with the same code returns the\nsame item and changes nothing (a retry is safe).",
    "operationId": "ensure_stock_item_route_api_v1_external_stock_items_post",
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/StockItemEnsure"
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Successful Response",
      "content": {
       "application/json": {
        "schema": {}
       }
      }
     },
     "422": {
      "description": "Validation Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      }
     }
    },
    "tags": [
     "stock-items"
    ]
   }
  },
  "/stock-issues": {
   "post": {
    "summary": "Create Stock Issue",
    "description": "Takes stock out to a project at the FIFO cost: Dr the item's COGS account\n(tagged to the project) / Cr its inventory account. The warehouse is not\nblocked by a missing layer: see _post_fifo_issue.",
    "operationId": "create_stock_issue_api_v1_external_stock_issues_post",
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/StockIssueCreate"
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Successful Response",
      "content": {
       "application/json": {
        "schema": {}
       }
      }
     },
     "422": {
      "description": "Validation Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      }
     }
    },
    "tags": [
     "stock-issues"
    ]
   }
  },
  "/stock-items/{item_id}/adjust-down": {
   "post": {
    "summary": "Adjust Stock Down",
    "description": "A stock-count decrease (2026-10-07): takes the units out of the oldest\nFIFO layers and posts Dr the adjustment (COGS) account / Cr the item's\ninventory account at the FIFO cost. No project. Not a sale.",
    "operationId": "adjust_stock_down_api_v1_external_stock_items__item_id__adjust_down_post",
    "parameters": [
     {
      "name": "item_id",
      "in": "path",
      "required": true,
      "schema": {
       "type": "string",
       "format": "uuid",
       "title": "Item Id"
      }
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/StockAdjustDown"
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Successful Response",
      "content": {
       "application/json": {
        "schema": {}
       }
      }
     },
     "422": {
      "description": "Validation Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      }
     }
    },
    "tags": [
     "stock-items"
    ]
   }
  },
  "/stock-items/{item_id}/opening": {
   "post": {
    "summary": "Create Stock Opening",
    "operationId": "create_stock_opening_api_v1_external_stock_items__item_id__opening_post",
    "parameters": [
     {
      "name": "item_id",
      "in": "path",
      "required": true,
      "schema": {
       "type": "string",
       "format": "uuid",
       "title": "Item Id"
      }
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/StockOpeningCreate"
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Successful Response",
      "content": {
       "application/json": {
        "schema": {}
       }
      }
     },
     "422": {
      "description": "Validation Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      }
     }
    },
    "tags": [
     "stock-items"
    ]
   }
  },
  "/invoices/{invoice_id}/send-email": {
   "post": {
    "summary": "Send Invoice Email Route",
    "operationId": "send_invoice_email_route_api_v1_external_invoices__invoice_id__send_email_post",
    "parameters": [
     {
      "name": "invoice_id",
      "in": "path",
      "required": true,
      "schema": {
       "type": "string",
       "format": "uuid",
       "title": "Invoice Id"
      }
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/InvoiceSendEmail"
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Successful Response",
      "content": {
       "application/json": {
        "schema": {}
       }
      }
     },
     "422": {
      "description": "Validation Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      }
     }
    },
    "tags": [
     "invoices"
    ]
   }
  },
  "/invoices/{invoice_id}/send-telegram": {
   "post": {
    "summary": "Send Invoice Telegram Route",
    "operationId": "send_invoice_telegram_route_api_v1_external_invoices__invoice_id__send_telegram_post",
    "parameters": [
     {
      "name": "invoice_id",
      "in": "path",
      "required": true,
      "schema": {
       "type": "string",
       "format": "uuid",
       "title": "Invoice Id"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "Successful Response",
      "content": {
       "application/json": {
        "schema": {}
       }
      }
     },
     "422": {
      "description": "Validation Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      }
     }
    },
    "tags": [
     "invoices"
    ]
   }
  },
  "/payments/{payment_id}/send-receipt": {
   "post": {
    "summary": "Send Receipt Route",
    "description": "Picks the channel automatically -- Telegram if the contact has one\nlinked, else email, same as the web app's own \"record and send\" button.",
    "operationId": "send_receipt_route_api_v1_external_payments__payment_id__send_receipt_post",
    "parameters": [
     {
      "name": "payment_id",
      "in": "path",
      "required": true,
      "schema": {
       "type": "string",
       "format": "uuid",
       "title": "Payment Id"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "Successful Response",
      "content": {
       "application/json": {
        "schema": {}
       }
      }
     },
     "422": {
      "description": "Validation Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      }
     }
    },
    "tags": [
     "payments"
    ]
   }
  },
  "/payments/{payment_id}/send-email": {
   "post": {
    "summary": "Send Receipt Email Route",
    "description": "Explicit-channel counterpart to POST /send-receipt above (which\nauto-picks Telegram vs email) -- same body shape as\nInvoiceSendEmail/send_invoice_email_route, for a caller (the Mini App)\nthat lets the person choose the channel and optionally type in a\ndifferent address rather than trusting the contact's own email on\nfile. Mirrors app/web/routes_email.py's payment_send_receipt_email\nexactly, just as JSON instead of a form-redirect.",
    "operationId": "send_receipt_email_route_api_v1_external_payments__payment_id__send_email_post",
    "parameters": [
     {
      "name": "payment_id",
      "in": "path",
      "required": true,
      "schema": {
       "type": "string",
       "format": "uuid",
       "title": "Payment Id"
      }
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/PaymentSendEmail"
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Successful Response",
      "content": {
       "application/json": {
        "schema": {}
       }
      }
     },
     "422": {
      "description": "Validation Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      }
     }
    },
    "tags": [
     "payments"
    ]
   }
  },
  "/payments/{payment_id}/send-telegram": {
   "post": {
    "summary": "Send Receipt Telegram Route",
    "description": "Explicit-channel counterpart to POST /send-receipt -- requires the\ncontact already have a linked Telegram chat (a bot can never message\nsomeone who hasn't messaged it first), same shape as\nsend_invoice_telegram_route. Mirrors app/web/routes_email.py's\npayment_send_receipt_telegram exactly.",
    "operationId": "send_receipt_telegram_route_api_v1_external_payments__payment_id__send_telegram_post",
    "parameters": [
     {
      "name": "payment_id",
      "in": "path",
      "required": true,
      "schema": {
       "type": "string",
       "format": "uuid",
       "title": "Payment Id"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "Successful Response",
      "content": {
       "application/json": {
        "schema": {}
       }
      }
     },
     "422": {
      "description": "Validation Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      }
     }
    },
    "tags": [
     "payments"
    ]
   }
  },
  "/employees": {
   "post": {
    "summary": "Create Employee Route",
    "operationId": "create_employee_route_api_v1_external_employees_post",
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/EmployeeCreate"
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Successful Response",
      "content": {
       "application/json": {
        "schema": {}
       }
      }
     },
     "422": {
      "description": "Validation Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      }
     }
    },
    "tags": [
     "employees"
    ]
   }
  },
  "/leave/employees": {
   "get": {
    "summary": "Search Employees",
    "description": "Lets the bot help a new user find their own employee record by\nname OR employee_no during the \"link my Telegram account\" flow --\nan employee number is the more reliable identifier (already known\nto the person from payroll/HR, no fuzzy-name ambiguity), so it's\nmatched exactly (case-insensitive) alongside the name substring\nsearch in the same `q` param.",
    "operationId": "search_employees_api_v1_external_leave_employees_get",
    "parameters": [
     {
      "name": "q",
      "in": "query",
      "required": false,
      "schema": {
       "anyOf": [
        {
         "type": "string"
        },
        {
         "type": "null"
        }
       ],
       "title": "Q"
      }
     },
     {
      "name": "unlinked_only",
      "in": "query",
      "required": false,
      "schema": {
       "type": "boolean",
       "default": false,
       "title": "Unlinked Only"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "Successful Response",
      "content": {
       "application/json": {
        "schema": {}
       }
      }
     },
     "422": {
      "description": "Validation Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      }
     }
    },
    "tags": [
     "leave"
    ]
   }
  },
  "/leave/employees/{employee_id}": {
   "get": {
    "summary": "Get Employee",
    "description": "Direct by-id lookup -- the bot's manager-approval flow only has an\nemployee_id off the leave request, not a name, until it looks one up\nhere.",
    "operationId": "get_employee_api_v1_external_leave_employees__employee_id__get",
    "parameters": [
     {
      "name": "employee_id",
      "in": "path",
      "required": true,
      "schema": {
       "type": "string",
       "format": "uuid",
       "title": "Employee Id"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "Successful Response",
      "content": {
       "application/json": {
        "schema": {}
       }
      }
     },
     "422": {
      "description": "Validation Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      }
     }
    },
    "tags": [
     "leave"
    ]
   }
  },
  "/leave/employees/by-telegram/{telegram_user_id}": {
   "get": {
    "summary": "Whoami",
    "description": "The bot calls this once per org it holds a key for, to resolve\n\"who is this Telegram user\" -- collecting hits across orgs itself\n(there's no cross-org lookup on the Ledger side, by design).",
    "operationId": "whoami_api_v1_external_leave_employees_by_telegram__telegram_user_id__get",
    "parameters": [
     {
      "name": "telegram_user_id",
      "in": "path",
      "required": true,
      "schema": {
       "type": "integer",
       "title": "Telegram User Id"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "Successful Response",
      "content": {
       "application/json": {
        "schema": {}
       }
      }
     },
     "422": {
      "description": "Validation Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      }
     }
    },
    "tags": [
     "leave"
    ]
   }
  },
  "/leave/employees/{employee_id}/telegram-link": {
   "post": {
    "summary": "Link Telegram",
    "operationId": "link_telegram_api_v1_external_leave_employees__employee_id__telegram_link_post",
    "parameters": [
     {
      "name": "employee_id",
      "in": "path",
      "required": true,
      "schema": {
       "type": "string",
       "format": "uuid",
       "title": "Employee Id"
      }
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/TelegramLink"
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Successful Response",
      "content": {
       "application/json": {
        "schema": {}
       }
      }
     },
     "422": {
      "description": "Validation Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      }
     }
    },
    "tags": [
     "leave"
    ]
   }
  },
  "/leave/employees/{employee_id}/start-date": {
   "post": {
    "summary": "Set Start Date",
    "description": "/calcredundancy's own \"no Start Date on file, save this one for\nnext time?\" step -- mirrors the SharePoint path's equivalent write.",
    "operationId": "set_start_date_api_v1_external_leave_employees__employee_id__start_date_post",
    "parameters": [
     {
      "name": "employee_id",
      "in": "path",
      "required": true,
      "schema": {
       "type": "string",
       "format": "uuid",
       "title": "Employee Id"
      }
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/StartDateUpdate"
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Successful Response",
      "content": {
       "application/json": {
        "schema": {}
       }
      }
     },
     "422": {
      "description": "Validation Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      }
     }
    },
    "tags": [
     "leave"
    ]
   }
  },
  "/leave/employees/{employee_id}/terminate": {
   "post": {
    "summary": "Terminate Employee",
    "description": "/calcredundancy's own \"mark as terminated?\" step, offered once a\nredundancy/termination calc is finalised -- mirrors the SharePoint\npath's equivalent EndDate write. Never called automatically; the bot\nonly reaches this after the admin explicitly confirms.",
    "operationId": "terminate_employee_api_v1_external_leave_employees__employee_id__terminate_post",
    "parameters": [
     {
      "name": "employee_id",
      "in": "path",
      "required": true,
      "schema": {
       "type": "string",
       "format": "uuid",
       "title": "Employee Id"
      }
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/TerminateEmployee"
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Successful Response",
      "content": {
       "application/json": {
        "schema": {}
       }
      }
     },
     "422": {
      "description": "Validation Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      }
     }
    },
    "tags": [
     "leave"
    ]
   }
  },
  "/leave/types": {
   "get": {
    "summary": "Get Leave Types",
    "operationId": "get_leave_types_api_v1_external_leave_types_get",
    "responses": {
     "200": {
      "description": "Successful Response",
      "content": {
       "application/json": {
        "schema": {}
       }
      }
     },
     "422": {
      "description": "Validation Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      }
     }
    },
    "tags": [
     "leave"
    ]
   }
  },
  "/leave/employees/{employee_id}/balances": {
   "get": {
    "summary": "Get Balances",
    "operationId": "get_balances_api_v1_external_leave_employees__employee_id__balances_get",
    "parameters": [
     {
      "name": "employee_id",
      "in": "path",
      "required": true,
      "schema": {
       "type": "string",
       "format": "uuid",
       "title": "Employee Id"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "Successful Response",
      "content": {
       "application/json": {
        "schema": {}
       }
      }
     },
     "422": {
      "description": "Validation Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      }
     }
    },
    "tags": [
     "leave"
    ]
   }
  },
  "/leave/holidays": {
   "get": {
    "summary": "Get Holidays",
    "operationId": "get_holidays_api_v1_external_leave_holidays_get",
    "parameters": [
     {
      "name": "from_date",
      "in": "query",
      "required": false,
      "schema": {
       "anyOf": [
        {
         "type": "string",
         "format": "date"
        },
        {
         "type": "null"
        }
       ],
       "title": "From Date"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "Successful Response",
      "content": {
       "application/json": {
        "schema": {}
       }
      }
     },
     "422": {
      "description": "Validation Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      }
     }
    },
    "tags": [
     "leave"
    ]
   },
   "post": {
    "summary": "Create Holiday",
    "operationId": "create_holiday_api_v1_external_leave_holidays_post",
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/HolidayCreate"
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Successful Response",
      "content": {
       "application/json": {
        "schema": {}
       }
      }
     },
     "422": {
      "description": "Validation Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      }
     }
    },
    "tags": [
     "leave"
    ]
   }
  },
  "/leave/requests": {
   "post": {
    "summary": "Create Request",
    "operationId": "create_request_api_v1_external_leave_requests_post",
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/LeaveRequestCreate"
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Successful Response",
      "content": {
       "application/json": {
        "schema": {}
       }
      }
     },
     "422": {
      "description": "Validation Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      }
     }
    },
    "tags": [
     "leave"
    ]
   },
   "get": {
    "summary": "Get Requests",
    "operationId": "get_requests_api_v1_external_leave_requests_get",
    "parameters": [
     {
      "name": "status",
      "in": "query",
      "required": false,
      "schema": {
       "anyOf": [
        {
         "type": "string"
        },
        {
         "type": "null"
        }
       ],
       "title": "Status"
      }
     },
     {
      "name": "employee_id",
      "in": "query",
      "required": false,
      "schema": {
       "anyOf": [
        {
         "type": "string",
         "format": "uuid"
        },
        {
         "type": "null"
        }
       ],
       "title": "Employee Id"
      }
     },
     {
      "name": "upcoming_only",
      "in": "query",
      "required": false,
      "schema": {
       "type": "boolean",
       "default": false,
       "title": "Upcoming Only"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "Successful Response",
      "content": {
       "application/json": {
        "schema": {}
       }
      }
     },
     "422": {
      "description": "Validation Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      }
     }
    },
    "tags": [
     "leave"
    ]
   }
  },
  "/leave/requests/{request_id}": {
   "get": {
    "summary": "Get Request",
    "description": "Direct by-id lookup -- the bot's manager-approval flow only has a\nrequest_id off a callback button until it needs to show whose\nrequest this is (before the reason text for a rejection arrives).",
    "operationId": "get_request_api_v1_external_leave_requests__request_id__get",
    "parameters": [
     {
      "name": "request_id",
      "in": "path",
      "required": true,
      "schema": {
       "type": "string",
       "format": "uuid",
       "title": "Request Id"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "Successful Response",
      "content": {
       "application/json": {
        "schema": {}
       }
      }
     },
     "422": {
      "description": "Validation Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      }
     }
    },
    "tags": [
     "leave"
    ]
   }
  },
  "/leave/requests/{request_id}/approve": {
   "post": {
    "summary": "Approve Request",
    "operationId": "approve_request_api_v1_external_leave_requests__request_id__approve_post",
    "parameters": [
     {
      "name": "request_id",
      "in": "path",
      "required": true,
      "schema": {
       "type": "string",
       "format": "uuid",
       "title": "Request Id"
      }
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/LeaveRequestDecision"
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Successful Response",
      "content": {
       "application/json": {
        "schema": {}
       }
      }
     },
     "422": {
      "description": "Validation Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      }
     }
    },
    "tags": [
     "leave"
    ]
   }
  },
  "/leave/requests/{request_id}/reject": {
   "post": {
    "summary": "Reject Request",
    "operationId": "reject_request_api_v1_external_leave_requests__request_id__reject_post",
    "parameters": [
     {
      "name": "request_id",
      "in": "path",
      "required": true,
      "schema": {
       "type": "string",
       "format": "uuid",
       "title": "Request Id"
      }
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/LeaveRequestDecision"
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Successful Response",
      "content": {
       "application/json": {
        "schema": {}
       }
      }
     },
     "422": {
      "description": "Validation Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      }
     }
    },
    "tags": [
     "leave"
    ]
   }
  },
  "/leave/adjustments": {
   "post": {
    "summary": "Create Adjustment",
    "description": "The ad-hoc \"days in lieu\" endpoint -- Joe: needed on an ad hoc\nbasis, doesn't exist in the old bot at all.",
    "operationId": "create_adjustment_api_v1_external_leave_adjustments_post",
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/LeaveAdjustmentCreate"
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Successful Response",
      "content": {
       "application/json": {
        "schema": {}
       }
      }
     },
     "422": {
      "description": "Validation Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/HTTPValidationError"
        }
       }
      }
     }
    },
    "tags": [
     "leave"
    ]
   }
  }
 },
 "components": {
  "schemas": {
   "AttachmentCreate": {
    "properties": {
     "file_name": {
      "type": "string",
      "title": "File Name"
     },
     "mime_type": {
      "type": "string",
      "title": "Mime Type"
     },
     "content_base64": {
      "type": "string",
      "title": "Content Base64"
     }
    },
    "type": "object",
    "required": [
     "file_name",
     "mime_type",
     "content_base64"
    ],
    "title": "AttachmentCreate"
   },
   "BankFeeCreate": {
    "properties": {
     "bank_account_id": {
      "type": "string",
      "format": "uuid",
      "title": "Bank Account Id"
     },
     "expense_account_id": {
      "type": "string",
      "format": "uuid",
      "title": "Expense Account Id"
     },
     "date": {
      "type": "string",
      "format": "date",
      "title": "Date"
     },
     "currency": {
      "type": "string",
      "title": "Currency"
     },
     "amount": {
      "anyOf": [
       {
        "type": "number"
       },
       {
        "type": "string",
        "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"
       }
      ],
      "title": "Amount"
     },
     "description": {
      "anyOf": [
       {
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Description"
     },
     "reference": {
      "type": "string",
      "title": "Reference"
     }
    },
    "type": "object",
    "required": [
     "bank_account_id",
     "expense_account_id",
     "date",
     "currency",
     "amount",
     "reference"
    ],
    "title": "BankFeeCreate"
   },
   "BillCreate": {
    "properties": {
     "contact_id": {
      "type": "string",
      "format": "uuid",
      "title": "Contact Id"
     },
     "date": {
      "type": "string",
      "format": "date",
      "title": "Date"
     },
     "currency": {
      "type": "string",
      "title": "Currency"
     },
     "lines": {
      "items": {
       "$ref": "#/components/schemas/BillPaymentLineCreate"
      },
      "type": "array",
      "title": "Lines"
     },
     "due_date": {
      "anyOf": [
       {
        "type": "string",
        "format": "date"
       },
       {
        "type": "null"
       }
      ],
      "title": "Due Date"
     },
     "reference": {
      "anyOf": [
       {
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Reference"
     },
     "their_ref": {
      "anyOf": [
       {
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Their Ref"
     },
     "memo": {
      "anyOf": [
       {
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Memo"
     }
    },
    "type": "object",
    "required": [
     "contact_id",
     "date",
     "currency",
     "lines"
    ],
    "title": "BillCreate"
   },
   "BillPaymentCreate": {
    "properties": {
     "contact_id": {
      "type": "string",
      "format": "uuid",
      "title": "Contact Id"
     },
     "date": {
      "type": "string",
      "format": "date",
      "title": "Date"
     },
     "currency": {
      "type": "string",
      "title": "Currency"
     },
     "bank_account_id": {
      "type": "string",
      "format": "uuid",
      "title": "Bank Account Id"
     },
     "lines": {
      "items": {
       "$ref": "#/components/schemas/BillPaymentLineCreate"
      },
      "type": "array",
      "title": "Lines"
     },
     "voucher_no": {
      "anyOf": [
       {
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Voucher No"
     },
     "reference": {
      "anyOf": [
       {
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Reference"
     },
     "memo": {
      "anyOf": [
       {
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Memo"
     },
     "advance_id": {
      "anyOf": [
       {
        "type": "string",
        "format": "uuid"
       },
       {
        "type": "null"
       }
      ],
      "title": "Advance Id"
     }
    },
    "type": "object",
    "required": [
     "contact_id",
     "date",
     "currency",
     "bank_account_id",
     "lines"
    ],
    "title": "BillPaymentCreate"
   },
   "BillPaymentLineCreate": {
    "properties": {
     "account_id": {
      "type": "string",
      "format": "uuid",
      "title": "Account Id"
     },
     "description": {
      "anyOf": [
       {
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Description"
     },
     "quantity": {
      "anyOf": [
       {
        "type": "number"
       },
       {
        "type": "string",
        "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"
       }
      ],
      "title": "Quantity",
      "default": "1"
     },
     "unit_price": {
      "anyOf": [
       {
        "type": "number"
       },
       {
        "type": "string",
        "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"
       }
      ],
      "title": "Unit Price"
     },
     "amount_type": {
      "type": "string",
      "title": "Amount Type",
      "default": "net"
     },
     "tax_rate_id": {
      "anyOf": [
       {
        "type": "string",
        "format": "uuid"
       },
       {
        "type": "null"
       }
      ],
      "title": "Tax Rate Id"
     },
     "wht_rate_id": {
      "anyOf": [
       {
        "type": "string",
        "format": "uuid"
       },
       {
        "type": "null"
       }
      ],
      "title": "Wht Rate Id"
     },
     "project_contact_id": {
      "anyOf": [
       {
        "type": "string",
        "format": "uuid"
       },
       {
        "type": "null"
       }
      ],
      "title": "Project Contact Id"
     },
     "item_id": {
      "anyOf": [
       {
        "type": "string",
        "format": "uuid"
       },
       {
        "type": "null"
       }
      ],
      "title": "Item Id"
     },
     "track_1_id": {
      "anyOf": [
       {
        "type": "string",
        "format": "uuid"
       },
       {
        "type": "null"
       }
      ],
      "title": "Track 1 Id"
     },
     "track_2_id": {
      "anyOf": [
       {
        "type": "string",
        "format": "uuid"
       },
       {
        "type": "null"
       }
      ],
      "title": "Track 2 Id"
     },
     "track_3_id": {
      "anyOf": [
       {
        "type": "string",
        "format": "uuid"
       },
       {
        "type": "null"
       }
      ],
      "title": "Track 3 Id"
     },
     "track_4_id": {
      "anyOf": [
       {
        "type": "string",
        "format": "uuid"
       },
       {
        "type": "null"
       }
      ],
      "title": "Track 4 Id"
     },
     "track_5_id": {
      "anyOf": [
       {
        "type": "string",
        "format": "uuid"
       },
       {
        "type": "null"
       }
      ],
      "title": "Track 5 Id"
     },
     "fringe_benefit_employee_id": {
      "anyOf": [
       {
        "type": "string",
        "format": "uuid"
       },
       {
        "type": "null"
       }
      ],
      "title": "Fringe Benefit Employee Id"
     }
    },
    "type": "object",
    "required": [
     "account_id",
     "unit_price"
    ],
    "title": "BillPaymentLineCreate"
   },
   "BillSettlementAllocationCreate": {
    "properties": {
     "bill_id": {
      "type": "string",
      "format": "uuid",
      "title": "Bill Id"
     },
     "amount": {
      "anyOf": [
       {
        "type": "number"
       },
       {
        "type": "string",
        "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"
       }
      ],
      "title": "Amount"
     }
    },
    "type": "object",
    "required": [
     "bill_id",
     "amount"
    ],
    "title": "BillSettlementAllocationCreate"
   },
   "BillSettlementCreate": {
    "properties": {
     "contact_id": {
      "type": "string",
      "format": "uuid",
      "title": "Contact Id"
     },
     "date": {
      "type": "string",
      "format": "date",
      "title": "Date"
     },
     "currency": {
      "type": "string",
      "title": "Currency"
     },
     "bank_account_id": {
      "type": "string",
      "format": "uuid",
      "title": "Bank Account Id"
     },
     "allocations": {
      "items": {
       "$ref": "#/components/schemas/BillSettlementAllocationCreate"
      },
      "type": "array",
      "title": "Allocations"
     },
     "voucher_no": {
      "anyOf": [
       {
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Voucher No"
     },
     "reference": {
      "anyOf": [
       {
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Reference"
     },
     "memo": {
      "anyOf": [
       {
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Memo"
     }
    },
    "type": "object",
    "required": [
     "contact_id",
     "date",
     "currency",
     "bank_account_id",
     "allocations"
    ],
    "title": "BillSettlementCreate"
   },
   "BillTheirRefUpdate": {
    "properties": {
     "their_ref": {
      "type": "string",
      "title": "Their Ref"
     }
    },
    "type": "object",
    "required": [
     "their_ref"
    ],
    "title": "BillTheirRefUpdate"
   },
   "CashAdvanceCreate": {
    "properties": {
     "name": {
      "type": "string",
      "title": "Name"
     },
     "holder_contact_id": {
      "type": "string",
      "format": "uuid",
      "title": "Holder Contact Id"
     },
     "project_contact_id": {
      "anyOf": [
       {
        "type": "string",
        "format": "uuid"
       },
       {
        "type": "null"
       }
      ],
      "title": "Project Contact Id"
     },
     "currency": {
      "type": "string",
      "title": "Currency",
      "default": "USD"
     },
     "initial_funding_amount": {
      "anyOf": [
       {
        "type": "number"
       },
       {
        "type": "string",
        "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"
       },
       {
        "type": "null"
       }
      ],
      "title": "Initial Funding Amount"
     },
     "initial_funding_bank_account_id": {
      "anyOf": [
       {
        "type": "string",
        "format": "uuid"
       },
       {
        "type": "null"
       }
      ],
      "title": "Initial Funding Bank Account Id"
     },
     "funding_date": {
      "anyOf": [
       {
        "type": "string",
        "format": "date"
       },
       {
        "type": "null"
       }
      ],
      "title": "Funding Date"
     },
     "existing_account_id": {
      "anyOf": [
       {
        "type": "string",
        "format": "uuid"
       },
       {
        "type": "null"
       }
      ],
      "title": "Existing Account Id"
     }
    },
    "type": "object",
    "required": [
     "name",
     "holder_contact_id"
    ],
    "title": "CashAdvanceCreate"
   },
   "CashAdvanceFund": {
    "properties": {
     "amount": {
      "anyOf": [
       {
        "type": "number"
       },
       {
        "type": "string",
        "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"
       }
      ],
      "title": "Amount"
     },
     "bank_account_id": {
      "type": "string",
      "format": "uuid",
      "title": "Bank Account Id"
     },
     "funding_date": {
      "type": "string",
      "format": "date",
      "title": "Funding Date"
     },
     "currency": {
      "type": "string",
      "title": "Currency",
      "default": "USD"
     }
    },
    "type": "object",
    "required": [
     "amount",
     "bank_account_id",
     "funding_date"
    ],
    "title": "CashAdvanceFund"
   },
   "CashAdvanceSettle": {
    "properties": {
     "bank_account_id": {
      "type": "string",
      "format": "uuid",
      "title": "Bank Account Id"
     },
     "settle_date": {
      "type": "string",
      "format": "date",
      "title": "Settle Date"
     },
     "currency": {
      "type": "string",
      "title": "Currency",
      "default": "USD"
     }
    },
    "type": "object",
    "required": [
     "bank_account_id",
     "settle_date"
    ],
    "title": "CashAdvanceSettle"
   },
   "ContactFindOrCreate": {
    "properties": {
     "name": {
      "type": "string",
      "title": "Name"
     },
     "type": {
      "type": "string",
      "title": "Type",
      "default": "customer"
     },
     "parent_contact_id": {
      "anyOf": [
       {
        "type": "string",
        "format": "uuid"
       },
       {
        "type": "null"
       }
      ],
      "title": "Parent Contact Id"
     },
     "tin": {
      "anyOf": [
       {
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Tin"
     },
     "addr_freeform_en": {
      "anyOf": [
       {
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Addr Freeform En"
     },
     "addr_freeform_kh": {
      "anyOf": [
       {
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Addr Freeform Kh"
     },
     "name_kh": {
      "anyOf": [
       {
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Name Kh"
     },
     "email": {
      "anyOf": [
       {
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Email"
     },
     "phone": {
      "anyOf": [
       {
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Phone"
     },
     "doc_language": {
      "type": "string",
      "title": "Doc Language",
      "default": "bilingual"
     }
    },
    "type": "object",
    "required": [
     "name"
    ],
    "title": "ContactFindOrCreate"
   },
   "ContactReparent": {
    "properties": {
     "new_parent_name": {
      "type": "string",
      "title": "New Parent Name"
     }
    },
    "type": "object",
    "required": [
     "new_parent_name"
    ],
    "title": "ContactReparent"
   },
   "CreditNoteApply": {
    "properties": {
     "invoice_id": {
      "anyOf": [
       {
        "type": "string",
        "format": "uuid"
       },
       {
        "type": "null"
       }
      ],
      "title": "Invoice Id"
     },
     "amount": {
      "anyOf": [
       {
        "type": "number"
       },
       {
        "type": "string",
        "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"
       },
       {
        "type": "null"
       }
      ],
      "title": "Amount"
     }
    },
    "type": "object",
    "title": "CreditNoteApply"
   },
   "CreditNoteCreate": {
    "properties": {
     "source_invoice_id": {
      "type": "string",
      "format": "uuid",
      "title": "Source Invoice Id"
     },
     "date": {
      "type": "string",
      "format": "date",
      "title": "Date"
     },
     "memo": {
      "anyOf": [
       {
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Memo"
     },
     "reference": {
      "anyOf": [
       {
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Reference"
     }
    },
    "type": "object",
    "required": [
     "source_invoice_id",
     "date"
    ],
    "title": "CreditNoteCreate"
   },
   "EmployeeCreate": {
    "properties": {
     "employee_no": {
      "type": "string",
      "title": "Employee No"
     },
     "name_en": {
      "type": "string",
      "title": "Name En"
     },
     "start_date": {
      "type": "string",
      "format": "date",
      "title": "Start Date"
     },
     "base_salary": {
      "anyOf": [
       {
        "type": "number"
       },
       {
        "type": "string",
        "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"
       }
      ],
      "title": "Base Salary"
     },
     "salary_ccy": {
      "type": "string",
      "title": "Salary Ccy"
     },
     "is_resident": {
      "type": "boolean",
      "title": "Is Resident",
      "default": true
     },
     "contract_type": {
      "type": "string",
      "title": "Contract Type",
      "default": "udc"
     },
     "dependants_spouse": {
      "type": "integer",
      "title": "Dependants Spouse",
      "default": 0
     },
     "dependants_children": {
      "type": "integer",
      "title": "Dependants Children",
      "default": 0
     },
     "contact_id": {
      "anyOf": [
       {
        "type": "string",
        "format": "uuid"
       },
       {
        "type": "null"
       }
      ],
      "title": "Contact Id"
     },
     "name_kh": {
      "anyOf": [
       {
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Name Kh"
     },
     "date_of_birth": {
      "anyOf": [
       {
        "type": "string",
        "format": "date"
       },
       {
        "type": "null"
       }
      ],
      "title": "Date Of Birth"
     },
     "gender": {
      "anyOf": [
       {
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Gender"
     },
     "national_id": {
      "anyOf": [
       {
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "National Id"
     },
     "nssf_number": {
      "anyOf": [
       {
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Nssf Number"
     },
     "nationality": {
      "anyOf": [
       {
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Nationality"
     },
     "bank_name": {
      "anyOf": [
       {
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Bank Name"
     },
     "bank_account_no": {
      "anyOf": [
       {
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Bank Account No"
     },
     "telegram_user_id": {
      "anyOf": [
       {
        "type": "integer"
       },
       {
        "type": "null"
       }
      ],
      "title": "Telegram User Id"
     },
     "email": {
      "anyOf": [
       {
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Email"
     }
    },
    "type": "object",
    "required": [
     "employee_no",
     "name_en",
     "start_date",
     "base_salary",
     "salary_ccy"
    ],
    "title": "EmployeeCreate"
   },
   "EstimateConvert": {
    "properties": {
     "percentage": {
      "anyOf": [
       {
        "type": "number"
       },
       {
        "type": "string",
        "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"
       }
      ],
      "title": "Percentage"
     },
     "invoice_date": {
      "type": "string",
      "format": "date",
      "title": "Invoice Date"
     },
     "stream_id": {
      "anyOf": [
       {
        "type": "string",
        "format": "uuid"
       },
       {
        "type": "null"
       }
      ],
      "title": "Stream Id"
     },
     "template_id": {
      "anyOf": [
       {
        "type": "string",
        "format": "uuid"
       },
       {
        "type": "null"
       }
      ],
      "title": "Template Id"
     },
     "due_date": {
      "anyOf": [
       {
        "type": "string",
        "format": "date"
       },
       {
        "type": "null"
       }
      ],
      "title": "Due Date"
     }
    },
    "type": "object",
    "required": [
     "percentage",
     "invoice_date"
    ],
    "title": "EstimateConvert"
   },
   "EstimateCreate": {
    "properties": {
     "contact_id": {
      "type": "string",
      "format": "uuid",
      "title": "Contact Id"
     },
     "date": {
      "type": "string",
      "format": "date",
      "title": "Date"
     },
     "currency": {
      "type": "string",
      "title": "Currency"
     },
     "lines": {
      "items": {
       "$ref": "#/components/schemas/EstimateLineCreate"
      },
      "type": "array",
      "title": "Lines"
     },
     "expiry_date": {
      "anyOf": [
       {
        "type": "string",
        "format": "date"
       },
       {
        "type": "null"
       }
      ],
      "title": "Expiry Date"
     },
     "memo": {
      "anyOf": [
       {
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Memo"
     },
     "message_to_recipient": {
      "anyOf": [
       {
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Message To Recipient"
     },
     "stream_id": {
      "anyOf": [
       {
        "type": "string",
        "format": "uuid"
       },
       {
        "type": "null"
       }
      ],
      "title": "Stream Id"
     }
    },
    "type": "object",
    "required": [
     "contact_id",
     "date",
     "currency",
     "lines"
    ],
    "title": "EstimateCreate"
   },
   "EstimateLineCreate": {
    "properties": {
     "account_id": {
      "type": "string",
      "format": "uuid",
      "title": "Account Id"
     },
     "description": {
      "anyOf": [
       {
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Description"
     },
     "quantity": {
      "anyOf": [
       {
        "type": "number"
       },
       {
        "type": "string",
        "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"
       }
      ],
      "title": "Quantity",
      "default": "1"
     },
     "unit_price": {
      "anyOf": [
       {
        "type": "number"
       },
       {
        "type": "string",
        "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"
       }
      ],
      "title": "Unit Price"
     },
     "tax_rate_id": {
      "anyOf": [
       {
        "type": "string",
        "format": "uuid"
       },
       {
        "type": "null"
       }
      ],
      "title": "Tax Rate Id"
     },
     "tax_rate_pct": {
      "anyOf": [
       {
        "type": "number"
       },
       {
        "type": "string",
        "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"
       }
      ],
      "title": "Tax Rate Pct",
      "default": "0"
     },
     "item_id": {
      "anyOf": [
       {
        "type": "string",
        "format": "uuid"
       },
       {
        "type": "null"
       }
      ],
      "title": "Item Id"
     },
     "track_1_id": {
      "anyOf": [
       {
        "type": "string",
        "format": "uuid"
       },
       {
        "type": "null"
       }
      ],
      "title": "Track 1 Id"
     },
     "track_2_id": {
      "anyOf": [
       {
        "type": "string",
        "format": "uuid"
       },
       {
        "type": "null"
       }
      ],
      "title": "Track 2 Id"
     },
     "track_3_id": {
      "anyOf": [
       {
        "type": "string",
        "format": "uuid"
       },
       {
        "type": "null"
       }
      ],
      "title": "Track 3 Id"
     },
     "track_4_id": {
      "anyOf": [
       {
        "type": "string",
        "format": "uuid"
       },
       {
        "type": "null"
       }
      ],
      "title": "Track 4 Id"
     },
     "track_5_id": {
      "anyOf": [
       {
        "type": "string",
        "format": "uuid"
       },
       {
        "type": "null"
       }
      ],
      "title": "Track 5 Id"
     }
    },
    "type": "object",
    "required": [
     "account_id",
     "unit_price"
    ],
    "title": "EstimateLineCreate"
   },
   "HTTPValidationError": {
    "properties": {
     "detail": {
      "items": {
       "$ref": "#/components/schemas/ValidationError"
      },
      "type": "array",
      "title": "Detail"
     }
    },
    "type": "object",
    "title": "HTTPValidationError"
   },
   "HolidayCreate": {
    "properties": {
     "date": {
      "type": "string",
      "format": "date",
      "title": "Date"
     },
     "name": {
      "type": "string",
      "title": "Name"
     }
    },
    "type": "object",
    "required": [
     "date",
     "name"
    ],
    "title": "HolidayCreate"
   },
   "InvoiceCreate": {
    "properties": {
     "contact_id": {
      "type": "string",
      "format": "uuid",
      "title": "Contact Id"
     },
     "date": {
      "type": "string",
      "format": "date",
      "title": "Date"
     },
     "currency": {
      "type": "string",
      "title": "Currency"
     },
     "lines": {
      "items": {
       "$ref": "#/components/schemas/InvoiceLineCreate"
      },
      "type": "array",
      "title": "Lines"
     },
     "discount": {
      "anyOf": [
       {
        "type": "number"
       },
       {
        "type": "string",
        "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"
       },
       {
        "type": "null"
       }
      ],
      "title": "Discount"
     },
     "stream_id": {
      "anyOf": [
       {
        "type": "string",
        "format": "uuid"
       },
       {
        "type": "null"
       }
      ],
      "title": "Stream Id"
     },
     "template_id": {
      "anyOf": [
       {
        "type": "string",
        "format": "uuid"
       },
       {
        "type": "null"
       }
      ],
      "title": "Template Id"
     },
     "due_date": {
      "anyOf": [
       {
        "type": "string",
        "format": "date"
       },
       {
        "type": "null"
       }
      ],
      "title": "Due Date"
     },
     "memo": {
      "anyOf": [
       {
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Memo"
     },
     "reference": {
      "anyOf": [
       {
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Reference"
     },
     "custom_field_1": {
      "anyOf": [
       {
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Custom Field 1"
     },
     "custom_field_2": {
      "anyOf": [
       {
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Custom Field 2"
     },
     "custom_field_3": {
      "anyOf": [
       {
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Custom Field 3"
     },
     "custom_field_4": {
      "anyOf": [
       {
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Custom Field 4"
     },
     "custom_field_5": {
      "anyOf": [
       {
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Custom Field 5"
     }
    },
    "type": "object",
    "required": [
     "contact_id",
     "date",
     "currency",
     "lines"
    ],
    "title": "InvoiceCreate"
   },
   "InvoiceLineCreate": {
    "properties": {
     "account_id": {
      "type": "string",
      "format": "uuid",
      "title": "Account Id"
     },
     "description": {
      "anyOf": [
       {
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Description"
     },
     "quantity": {
      "anyOf": [
       {
        "type": "number"
       },
       {
        "type": "string",
        "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"
       }
      ],
      "title": "Quantity",
      "default": "1"
     },
     "unit_price": {
      "anyOf": [
       {
        "type": "number"
       },
       {
        "type": "string",
        "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"
       }
      ],
      "title": "Unit Price"
     },
     "tax_rate_id": {
      "anyOf": [
       {
        "type": "string",
        "format": "uuid"
       },
       {
        "type": "null"
       }
      ],
      "title": "Tax Rate Id"
     },
     "tax_rate_pct": {
      "anyOf": [
       {
        "type": "number"
       },
       {
        "type": "string",
        "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"
       }
      ],
      "title": "Tax Rate Pct",
      "default": "0"
     },
     "item_id": {
      "anyOf": [
       {
        "type": "string",
        "format": "uuid"
       },
       {
        "type": "null"
       }
      ],
      "title": "Item Id"
     },
     "is_discount": {
      "type": "boolean",
      "title": "Is Discount",
      "default": false
     },
     "track_1_id": {
      "anyOf": [
       {
        "type": "string",
        "format": "uuid"
       },
       {
        "type": "null"
       }
      ],
      "title": "Track 1 Id"
     },
     "track_2_id": {
      "anyOf": [
       {
        "type": "string",
        "format": "uuid"
       },
       {
        "type": "null"
       }
      ],
      "title": "Track 2 Id"
     },
     "track_3_id": {
      "anyOf": [
       {
        "type": "string",
        "format": "uuid"
       },
       {
        "type": "null"
       }
      ],
      "title": "Track 3 Id"
     },
     "track_4_id": {
      "anyOf": [
       {
        "type": "string",
        "format": "uuid"
       },
       {
        "type": "null"
       }
      ],
      "title": "Track 4 Id"
     },
     "track_5_id": {
      "anyOf": [
       {
        "type": "string",
        "format": "uuid"
       },
       {
        "type": "null"
       }
      ],
      "title": "Track 5 Id"
     }
    },
    "type": "object",
    "required": [
     "account_id",
     "unit_price"
    ],
    "title": "InvoiceLineCreate"
   },
   "InvoiceReissue": {
    "properties": {
     "stream_id": {
      "anyOf": [
       {
        "type": "string",
        "format": "uuid"
       },
       {
        "type": "null"
       }
      ],
      "title": "Stream Id"
     }
    },
    "type": "object",
    "title": "InvoiceReissue"
   },
   "InvoiceSendEmail": {
    "properties": {
     "key": {
      "type": "string",
      "title": "Key",
      "default": "invoice_sent"
     },
     "recipient_override": {
      "anyOf": [
       {
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Recipient Override"
     },
     "cc": {
      "anyOf": [
       {
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Cc"
     },
     "bcc": {
      "anyOf": [
       {
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Bcc"
     }
    },
    "type": "object",
    "title": "InvoiceSendEmail"
   },
   "LeaveAdjustmentCreate": {
    "properties": {
     "employee_id": {
      "type": "string",
      "format": "uuid",
      "title": "Employee Id"
     },
     "leave_type_id": {
      "type": "string",
      "format": "uuid",
      "title": "Leave Type Id"
     },
     "delta": {
      "anyOf": [
       {
        "type": "number"
       },
       {
        "type": "string",
        "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"
       }
      ],
      "title": "Delta"
     },
     "reason": {
      "type": "string",
      "title": "Reason"
     },
     "actor_label": {
      "type": "string",
      "title": "Actor Label"
     }
    },
    "type": "object",
    "required": [
     "employee_id",
     "leave_type_id",
     "delta",
     "reason",
     "actor_label"
    ],
    "title": "LeaveAdjustmentCreate"
   },
   "LeaveRequestCreate": {
    "properties": {
     "employee_id": {
      "type": "string",
      "format": "uuid",
      "title": "Employee Id"
     },
     "leave_type_id": {
      "type": "string",
      "format": "uuid",
      "title": "Leave Type Id"
     },
     "start_date": {
      "type": "string",
      "format": "date",
      "title": "Start Date"
     },
     "end_date": {
      "type": "string",
      "format": "date",
      "title": "End Date"
     },
     "half_day": {
      "type": "string",
      "title": "Half Day",
      "default": "none"
     },
     "reason": {
      "anyOf": [
       {
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Reason"
     }
    },
    "type": "object",
    "required": [
     "employee_id",
     "leave_type_id",
     "start_date",
     "end_date"
    ],
    "title": "LeaveRequestCreate"
   },
   "LeaveRequestDecision": {
    "properties": {
     "actor_label": {
      "type": "string",
      "title": "Actor Label"
     },
     "rejection_reason": {
      "anyOf": [
       {
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Rejection Reason"
     }
    },
    "type": "object",
    "required": [
     "actor_label"
    ],
    "title": "LeaveRequestDecision"
   },
   "ManualJournalCreate": {
    "properties": {
     "date": {
      "type": "string",
      "format": "date",
      "title": "Date"
     },
     "currency": {
      "type": "string",
      "title": "Currency"
     },
     "lines": {
      "items": {
       "$ref": "#/components/schemas/ManualJournalLine"
      },
      "type": "array",
      "title": "Lines"
     },
     "narration": {
      "anyOf": [
       {
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Narration"
     },
     "reference": {
      "anyOf": [
       {
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Reference"
     }
    },
    "type": "object",
    "required": [
     "date",
     "currency",
     "lines"
    ],
    "title": "ManualJournalCreate"
   },
   "ManualJournalLine": {
    "properties": {
     "account_id": {
      "type": "string",
      "format": "uuid",
      "title": "Account Id"
     },
     "debit": {
      "anyOf": [
       {
        "type": "number"
       },
       {
        "type": "string",
        "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"
       }
      ],
      "title": "Debit",
      "default": "0"
     },
     "credit": {
      "anyOf": [
       {
        "type": "number"
       },
       {
        "type": "string",
        "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"
       }
      ],
      "title": "Credit",
      "default": "0"
     },
     "description": {
      "anyOf": [
       {
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Description"
     },
     "contact_id": {
      "anyOf": [
       {
        "type": "string",
        "format": "uuid"
       },
       {
        "type": "null"
       }
      ],
      "title": "Contact Id"
     },
     "project_contact_id": {
      "anyOf": [
       {
        "type": "string",
        "format": "uuid"
       },
       {
        "type": "null"
       }
      ],
      "title": "Project Contact Id"
     }
    },
    "type": "object",
    "required": [
     "account_id"
    ],
    "title": "ManualJournalLine"
   },
   "PatentExtractRequest": {
    "properties": {
     "content_base64": {
      "type": "string",
      "title": "Content Base64"
     },
     "mime_type": {
      "type": "string",
      "title": "Mime Type",
      "default": "application/pdf"
     }
    },
    "type": "object",
    "required": [
     "content_base64"
    ],
    "title": "PatentExtractRequest"
   },
   "PaymentSendEmail": {
    "properties": {
     "recipient_override": {
      "anyOf": [
       {
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Recipient Override"
     },
     "cc": {
      "anyOf": [
       {
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Cc"
     },
     "bcc": {
      "anyOf": [
       {
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Bcc"
     }
    },
    "type": "object",
    "title": "PaymentSendEmail"
   },
   "ProjectCreate": {
    "properties": {
     "customer_contact_id": {
      "type": "string",
      "format": "uuid",
      "title": "Customer Contact Id"
     },
     "name": {
      "type": "string",
      "title": "Name"
     }
    },
    "type": "object",
    "required": [
     "customer_contact_id",
     "name"
    ],
    "title": "ProjectCreate"
   },
   "ReceiptAllocationCreate": {
    "properties": {
     "invoice_id": {
      "type": "string",
      "format": "uuid",
      "title": "Invoice Id"
     },
     "amount": {
      "anyOf": [
       {
        "type": "number"
       },
       {
        "type": "string",
        "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"
       }
      ],
      "title": "Amount"
     }
    },
    "type": "object",
    "required": [
     "invoice_id",
     "amount"
    ],
    "title": "ReceiptAllocationCreate"
   },
   "ReceiptCreate": {
    "properties": {
     "contact_id": {
      "type": "string",
      "format": "uuid",
      "title": "Contact Id"
     },
     "date": {
      "type": "string",
      "format": "date",
      "title": "Date"
     },
     "currency": {
      "type": "string",
      "title": "Currency"
     },
     "bank_account_id": {
      "type": "string",
      "format": "uuid",
      "title": "Bank Account Id"
     },
     "allocations": {
      "items": {
       "$ref": "#/components/schemas/ReceiptAllocationCreate"
      },
      "type": "array",
      "title": "Allocations"
     },
     "amount": {
      "anyOf": [
       {
        "type": "number"
       },
       {
        "type": "string",
        "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"
       },
       {
        "type": "null"
       }
      ],
      "title": "Amount"
     },
     "receipt_no": {
      "anyOf": [
       {
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Receipt No"
     },
     "reference": {
      "anyOf": [
       {
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Reference"
     },
     "memo": {
      "anyOf": [
       {
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Memo"
     }
    },
    "type": "object",
    "required": [
     "contact_id",
     "date",
     "currency",
     "bank_account_id",
     "allocations"
    ],
    "title": "ReceiptCreate"
   },
   "SsoLinkRequest": {
    "properties": {
     "product": {
      "type": "string",
      "title": "Product"
     },
     "email": {
      "type": "string",
      "title": "Email"
     }
    },
    "type": "object",
    "required": [
     "product",
     "email"
    ],
    "title": "SsoLinkRequest"
   },
   "StartDateUpdate": {
    "properties": {
     "start_date": {
      "type": "string",
      "format": "date",
      "title": "Start Date"
     }
    },
    "type": "object",
    "required": [
     "start_date"
    ],
    "title": "StartDateUpdate"
   },
   "StockAdjustDown": {
    "properties": {
     "quantity": {
      "anyOf": [
       {
        "type": "number"
       },
       {
        "type": "string",
        "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"
       }
      ],
      "title": "Quantity"
     },
     "date": {
      "type": "string",
      "format": "date",
      "title": "Date"
     },
     "reference": {
      "type": "string",
      "title": "Reference"
     },
     "narration": {
      "anyOf": [
       {
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Narration"
     },
     "debit_account_id": {
      "anyOf": [
       {
        "type": "string",
        "format": "uuid"
       },
       {
        "type": "null"
       }
      ],
      "title": "Debit Account Id"
     }
    },
    "type": "object",
    "required": [
     "quantity",
     "date",
     "reference"
    ],
    "title": "StockAdjustDown"
   },
   "StockIssueCreate": {
    "properties": {
     "item_id": {
      "type": "string",
      "format": "uuid",
      "title": "Item Id"
     },
     "quantity": {
      "anyOf": [
       {
        "type": "number"
       },
       {
        "type": "string",
        "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"
       }
      ],
      "title": "Quantity"
     },
     "date": {
      "type": "string",
      "format": "date",
      "title": "Date"
     },
     "reference": {
      "type": "string",
      "title": "Reference"
     },
     "project_contact_id": {
      "anyOf": [
       {
        "type": "string",
        "format": "uuid"
       },
       {
        "type": "null"
       }
      ],
      "title": "Project Contact Id"
     },
     "narration": {
      "anyOf": [
       {
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Narration"
     }
    },
    "type": "object",
    "required": [
     "item_id",
     "quantity",
     "date",
     "reference"
    ],
    "title": "StockIssueCreate"
   },
   "StockItemEnsure": {
    "properties": {
     "code": {
      "type": "string",
      "title": "Code"
     },
     "name": {
      "type": "string",
      "title": "Name"
     },
     "uom": {
      "anyOf": [
       {
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Uom"
     },
     "inventory_account_id": {
      "type": "string",
      "format": "uuid",
      "title": "Inventory Account Id"
     },
     "cogs_account_id": {
      "type": "string",
      "format": "uuid",
      "title": "Cogs Account Id"
     },
     "purchase_price": {
      "anyOf": [
       {
        "type": "number"
       },
       {
        "type": "string",
        "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"
       },
       {
        "type": "null"
       }
      ],
      "title": "Purchase Price"
     }
    },
    "type": "object",
    "required": [
     "code",
     "name",
     "inventory_account_id",
     "cogs_account_id"
    ],
    "title": "StockItemEnsure"
   },
   "StockOpeningCreate": {
    "properties": {
     "quantity": {
      "anyOf": [
       {
        "type": "number"
       },
       {
        "type": "string",
        "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"
       }
      ],
      "title": "Quantity"
     },
     "unit_cost": {
      "anyOf": [
       {
        "type": "number"
       },
       {
        "type": "string",
        "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"
       }
      ],
      "title": "Unit Cost"
     },
     "date": {
      "type": "string",
      "format": "date",
      "title": "Date"
     },
     "credit_account_id": {
      "anyOf": [
       {
        "type": "string",
        "format": "uuid"
       },
       {
        "type": "null"
       }
      ],
      "title": "Credit Account Id"
     },
     "reference": {
      "anyOf": [
       {
        "type": "string"
       },
       {
        "type": "null"
       }
      ],
      "title": "Reference"
     }
    },
    "type": "object",
    "required": [
     "quantity",
     "unit_cost",
     "date"
    ],
    "title": "StockOpeningCreate"
   },
   "TelegramLink": {
    "properties": {
     "telegram_user_id": {
      "type": "integer",
      "title": "Telegram User Id"
     }
    },
    "type": "object",
    "required": [
     "telegram_user_id"
    ],
    "title": "TelegramLink"
   },
   "TerminateEmployee": {
    "properties": {
     "end_date": {
      "type": "string",
      "format": "date",
      "title": "End Date"
     }
    },
    "type": "object",
    "required": [
     "end_date"
    ],
    "title": "TerminateEmployee"
   },
   "TransferCreate": {
    "properties": {
     "from_account_id": {
      "type": "string",
      "format": "uuid",
      "title": "From Account Id"
     },
     "to_account_id": {
      "type": "string",
      "format": "uuid",
      "title": "To Account Id"
     },
     "date": {
      "type": "string",
      "format": "date",
      "title": "Date"
     },
     "currency": {
      "type": "string",
      "title": "Currency"
     },
     "amount": {
      "anyOf": [
       {
        "type": "number"
       },
       {
        "type": "string",
        "pattern": "^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"
       }
      ],
      "title": "Amount"
     },
     "reference": {
      "type": "string",
      "title": "Reference"
     }
    },
    "type": "object",
    "required": [
     "from_account_id",
     "to_account_id",
     "date",
     "currency",
     "amount",
     "reference"
    ],
    "title": "TransferCreate"
   },
   "ValidationError": {
    "properties": {
     "loc": {
      "items": {
       "anyOf": [
        {
         "type": "string"
        },
        {
         "type": "integer"
        }
       ]
      },
      "type": "array",
      "title": "Location"
     },
     "msg": {
      "type": "string",
      "title": "Message"
     },
     "type": {
      "type": "string",
      "title": "Error Type"
     },
     "input": {
      "title": "Input"
     },
     "ctx": {
      "type": "object",
      "title": "Context"
     }
    },
    "type": "object",
    "required": [
     "loc",
     "msg",
     "type"
    ],
    "title": "ValidationError"
   }
  },
  "securitySchemes": {
   "ApiKeyAuth": {
    "type": "apiKey",
    "in": "header",
    "name": "X-Api-Key"
   }
  }
 }
}