openapi: 3.0.3
info:
  title: Katalis UK Business Intelligence APIs
  version: 1.9.0
  description: >
    UK company checks from the official record, on one key: the checking record a developer can trust before
    their business works with, pays, hires from or bids against a UK company. Companies House (profiles, officers,
    PSCs, charges, filings, filed accounts read into plain figures), the Payment Practices reports and the PPN 018
    tests, public contracts won, the Register of licensed sponsors, HMRC's anti-money-laundering register, the SRA,
    CQC and charity registers (each with a daily change feed), the UK Sanctions List, the gender pay gap, Environment
    Agency registrations, directors' and PSCs' ID verification against their deadlines, every deadline a company has
    (also as a calendar feed), and a change feed with signed webhooks for any company you watch. Alongside: the
    document readers (Modern Slavery statements, Ofsted and CQC reports, charity accounts), the Business Score and fit
    assessment, tender matching, AI visibility, and the design APIs (brand kit, generative art, effects, logos).
    Every reply says where it came from and carries the source's attribution; people's details go only to verified
    accounts. Get a free API key (100 credits a month, no card) at https://meetkatalis.com/developers and send it as
    the `x-api-key` header. Also: every API as Markdown at https://meetkatalis.com/apis.md, an MCP server for AI
    agents at https://meetkatalis.com/mcp, live status at https://meetkatalis.com/apis/about.

    Response conventions (since 2 October 2026): keys are camelCase on every endpoint; a registered
    company is `company: { name, number, status, statusDetail?, filingsOverdue?, ... }` on the register
    endpoints; the enrichment family (enrich, score, assess, brand-kit, ai-visibility, tender-match) keeps
    `business` / `profile` with `companyNumber` where one was matched, because its subject may be any
    business; an error reply that echoes what was asked for says `query`. Every error carries `code` and
    `requestId`; every reply an `x-request-id` header (since 1.9.0, 10 October 2026, the keyless and demo calls and the
    unknown-path 404 included); a charged reply `x-credits-remaining`; a reply that gave credits back
    `x-credits-refunded`; an error reply that was charged (a lookup that found nothing still ran) says `creditsCharged`.
    `company` carries `status` (and `statusDetail`, `filingsOverdue` when they apply) wherever
    the register was read for the reply; the report-based APIs (payment practices, PPN 018, gender pay gap,
    will-they-pay) give the name and number their report carries. Rate limits: monthly credit quota per plan
    plus a burst ceiling of 60 requests/minute per key (429 with no credit used). A request that
    fails on our side (any 5xx, including one that runs past its time limit) gives its credits
    back, and the x-credits-refunded header says how many; in a batch, items we couldn't read are
    credited back and creditsRefunded says how many.
    Contains public sector information licensed under the Open Government Licence v3.0.
    Environment Agency register data is used under the Environment Agency Conditional Licence.
  contact:
    name: Katalis
    url: https://meetkatalis.com/apis
    email: katalis@meetkatalis.com
  license:
    name: Katalis API Terms of Use (data from public sources keeps its own licence, e.g. the Open Government Licence v3.0)
    url: https://meetkatalis.com/apis/terms
  x-logo:
    url: https://meetkatalis.com/icon.svg
    altText: Katalis
servers:
  - url: https://meetkatalis.com
paths:
  /api/v1/enrich:
    get:
      operationId: enrichCompany
      x-credits: 2
      summary: Enrich a UK business
      description: Returns a verified profile for a UK company name or domain.
      security:
        - ApiKeyAuth: []
      parameters:
        - name: q
          in: query
          required: true
          description: Company name or domain (e.g. "Greggs" or "greggs.co.uk").
          schema: { type: string, maxLength: 200 }
        - name: location
          in: query
          required: false
          description: Optional town/city to disambiguate.
          schema: { type: string, maxLength: 120 }
      responses:
        "200":
          description: Enriched profile
          content:
            application/json:
              schema: { $ref: "#/components/schemas/EnrichResponse" }
        "400": { description: "Missing or invalid q" }
        "401": { description: "Missing or wrong x-api-key" }
        "429": { description: "Monthly quota reached (upgrade) OR the 60 req/min burst brake (retry — burst 429s never consume credits)" }
        "503": { description: "Temporarily unavailable — retry" }
  /api/v1/score:
    get:
      operationId: businessScore
      x-credits: 10
      summary: Business Score for a UK business
      description: Enriches then grades the business outside-in, with a prioritised action plan.
      security:
        - ApiKeyAuth: []
      parameters:
        - name: q
          in: query
          required: true
          schema: { type: string, maxLength: 200 }
        - name: location
          in: query
          required: false
          schema: { type: string, maxLength: 120 }
      responses:
        "200":
          description: Score, per-category findings and action plan
        "400": { description: "Missing or invalid q" }
        "401": { description: "Missing or wrong x-api-key" }
        "429": { description: "Monthly quota reached OR burst brake (60/min)" }
        "503": { description: "Temporarily unavailable — retry" }
  /api/v1/batch:
    post:
      operationId: enrichBatch
      x-credits: "2 per item (one atomic charge for the batch)"
      summary: Batch-enrich up to 25 UK businesses
      description: One call, one atomic credit consume (2 credits per item, as company enrichment). Bad items return found:false in place.
      security:
        - ApiKeyAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [items]
              properties:
                items:
                  type: array
                  minItems: 1
                  maxItems: 25
                  items: { type: string }
                location: { type: string }
      responses:
        "200": { description: "Results array in input order, with creditsUsed" }
        "400": { description: "Missing or invalid items array" }
        "401": { description: "Missing or wrong x-api-key" }
        "429": { description: "Not enough credits for the whole batch, or burst brake" }
        "503": { description: "Temporarily unavailable — retry" }
  /api/v1/assess:
    post:
      operationId: assessFit
      x-credits: 25
      summary: Fit & approach assessment (premium, 25 credits)
      description: >
        A target business plus what YOU sell → fit score, the target's observable signals
        mapped to your offer, a factual approach angle and an editable opener draft.
        Grounded in the verified profile; never predicts receptiveness.
      security:
        - ApiKeyAuth: []
      requestBody:
        content:
          application/json:
            schema:
              type: object
              required: [target, offer]
              properties:
                target: { type: string, description: Company name or domain }
                offer: { type: string, description: What you sell and who it's for }
                location: { type: string }
      responses:
        "200": { description: "fitScore, fitReasons, signalsForYou, approachAngle, openerDraft, watchOuts" }
        "400": { description: "Missing target or offer" }
        "401": { description: "Missing or wrong x-api-key" }
        "422": { description: "Target could not be confidently identified" }
        "429": { description: "Monthly quota reached OR burst brake (60/min)" }
        "503": { description: "Temporarily unavailable — retry" }
  /api/v1/company-search:
    get:
      operationId: searchCompanies
      x-credits: 1
      summary: Search UK registered companies
      description: Companies House search, cleaned and confidence-ranked (company-level only, never officer personal data).
      security:
        - ApiKeyAuth: []
      parameters:
        - name: q
          in: query
          required: true
          schema: { type: string, maxLength: 200 }
        - name: limit
          in: query
          required: false
          description: Max results (1-50, default 10).
          schema: { type: integer, minimum: 1, maximum: 50, default: 10 }
      responses:
        "200": { description: "Ranked company matches with 0-1 match confidence" }
        "400": { description: "Missing or invalid q" }
        "401": { description: "Missing or wrong x-api-key" }
        "429": { description: "Monthly quota reached OR burst brake (60/min)" }
        "503": { description: "Temporarily unavailable — retry" }
  /api/v1/industry-classifier:
    get:
      operationId: classifyIndustry
      x-credits: 2
      summary: Classify a business's industry
      description: Returns official SIC codes, a plain-English industry label, what they do and a confidence rating.
      security:
        - ApiKeyAuth: []
      parameters:
        - name: q
          in: query
          required: true
          schema: { type: string, maxLength: 200 }
      responses:
        "200": { description: "Industry, SIC codes, description, confidence" }
        "400": { description: "Missing or invalid q" }
        "401": { description: "Missing or wrong x-api-key" }
        "429": { description: "Monthly quota reached OR burst brake (60/min)" }
        "503": { description: "Temporarily unavailable — retry" }
  /api/v1/accessibility-audit:
    get:
      operationId: auditAccessibility
      x-credits: 2
      summary: Accessibility audit of a web page
      description: >
        Deterministic WCAG-flavoured checks over the rendered page — missing alt text,
        unlabelled forms, blocked zoom, nameless buttons and more. Honest diagnosis:
        automated checks catch only a minority of WCAG issues and every response says so.
        Never a compliance certification.
      security:
        - ApiKeyAuth: []
      parameters:
        - name: url
          in: query
          required: true
          description: The page to audit.
          schema: { type: string, maxLength: 500 }
      responses:
        "200": { description: "Score, findings (each tied to a WCAG criterion) and passed checks" }
        "400": { description: "Missing or invalid url" }
        "401": { description: "Missing or wrong x-api-key" }
        "422": { description: "Page could not be fetched" }
        "429": { description: "Monthly quota reached OR burst brake (60/min)" }
        "503": { description: "Temporarily unavailable — retry" }
  /api/v1/modern-slavery:
    get:
      operationId: scoreModernSlaveryStatementByUrl
      x-credits: 4
      summary: Score a Modern Slavery Act statement (by URL)
      description: GET variant — pass the statement's URL (HTML or PDF); we fetch and read it.
      security:
        - ApiKeyAuth: []
      parameters:
        - name: url
          in: query
          required: true
          schema: { type: string, maxLength: 500 }
      responses:
        "200": { description: "Structured six-area scoring with sign-off compliance and gaps" }
        "400": { description: "Missing url" }
        "401": { description: "Missing or wrong x-api-key" }
        "422": { description: "Document could not be fetched or read" }
        "429": { description: "Monthly quota reached OR burst brake (60/min)" }
        "503": { description: "Temporarily unavailable — retry" }
    post:
      operationId: scoreModernSlaveryStatement
      x-credits: 4
      summary: Score a Modern Slavery Act statement
      description: Turns a UK Modern Slavery Act s.54 statement into structured, quality-scored JSON.
      security:
        - ApiKeyAuth: []
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                text: { type: string, description: The statement text }
                url: { type: string, description: An HTML or PDF statement URL (alternative to text) }
      responses:
        "200": { description: "Structured six-area scoring with sign-off compliance and gaps" }
        "400": { description: "Missing text and url" }
        "401": { description: "Missing or wrong x-api-key" }
        "422": { description: "Document could not be fetched or read" }
        "429": { description: "Monthly quota reached OR burst brake (60/min)" }
        "503": { description: "Temporarily unavailable — retry" }
  /api/v1/ofsted-report:
    get:
      operationId: structureOfstedReportByUrl
      x-credits: 4
      summary: Structure an Ofsted report card (by URL)
      description: GET variant — pass the report's URL (HTML or PDF); we fetch and read it.
      security:
        - ApiKeyAuth: []
      parameters:
        - name: url
          in: query
          required: true
          schema: { type: string, maxLength: 500 }
      responses:
        "200": { description: "Structured grades, summary and themes" }
        "400": { description: "Missing url" }
        "401": { description: "Missing or wrong x-api-key" }
        "422": { description: "Document could not be fetched or read" }
        "429": { description: "Monthly quota reached OR burst brake (60/min)" }
        "503": { description: "Temporarily unavailable — retry" }
    post:
      operationId: structureOfstedReport
      x-credits: 4
      summary: Structure an Ofsted report card
      description: Feed in an Ofsted report and get graded areas, a plain-English summary, strengths and safeguarding status.
      security:
        - ApiKeyAuth: []
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                text: { type: string }
                url: { type: string }
      responses:
        "200": { description: "Structured grades, summary and themes" }
        "400": { description: "Missing text and url" }
        "401": { description: "Missing or wrong x-api-key" }
        "422": { description: "Document could not be fetched or read" }
        "429": { description: "Monthly quota reached OR burst brake (60/min)" }
        "503": { description: "Temporarily unavailable — retry" }
  /api/v1/cqc-report:
    get:
      operationId: structureCqcReportByUrl
      x-credits: 4
      summary: Structure a CQC inspection report (by URL)
      description: GET variant — pass the report's URL (HTML or PDF, including CQC's own site); we fetch and read it.
      security:
        - ApiKeyAuth: []
      parameters:
        - name: url
          in: query
          required: true
          schema: { type: string, maxLength: 500 }
      responses:
        "200": { description: "Structured ratings, summary, concerns" }
        "400": { description: "Missing url" }
        "401": { description: "Missing or wrong x-api-key" }
        "422": { description: "Document could not be fetched or read" }
        "429": { description: "Monthly quota reached OR burst brake (60/min)" }
        "503": { description: "Temporarily unavailable — retry" }
    post:
      operationId: structureCqcReport
      x-credits: 4
      summary: Structure a CQC inspection report
      description: Overall rating, the five key questions, strengths, concerns and enforcement — structured from the report text.
      security:
        - ApiKeyAuth: []
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                text: { type: string }
                url: { type: string }
      responses:
        "200": { description: "Structured ratings, summary, concerns" }
        "400": { description: "Missing text and url" }
        "401": { description: "Missing or wrong x-api-key" }
        "422": { description: "Document could not be fetched or read" }
        "429": { description: "Monthly quota reached OR burst brake (60/min)" }
        "503": { description: "Temporarily unavailable — retry" }
  /api/v1/charity-report:
    get:
      operationId: structureCharityReportByUrl
      x-credits: 4
      summary: Structure a charity trustees' report (by URL)
      description: GET variant — pass the report's URL (HTML or PDF); we fetch and read it.
      security:
        - ApiKeyAuth: []
      parameters:
        - name: url
          in: query
          required: true
          schema: { type: string, maxLength: 500 }
      responses:
        "200": { description: "Structured due-diligence read with transparency score" }
        "400": { description: "Missing url" }
        "401": { description: "Missing or wrong x-api-key" }
        "422": { description: "Document could not be fetched or read" }
        "429": { description: "Monthly quota reached OR burst brake (60/min)" }
        "503": { description: "Temporarily unavailable — retry" }
    post:
      operationId: structureCharityReport
      x-credits: 4
      summary: Structure a charity trustees' report for due diligence
      description: Finances, governance signals, delivery, flags, transparency score and the gaps a funder would ask about.
      security:
        - ApiKeyAuth: []
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                text: { type: string }
                url: { type: string }
      responses:
        "200": { description: "Structured due-diligence read with transparency score" }
        "400": { description: "Missing text and url" }
        "401": { description: "Missing or wrong x-api-key" }
        "422": { description: "Document could not be fetched or read" }
        "429": { description: "Monthly quota reached OR burst brake (60/min)" }
        "503": { description: "Temporarily unavailable — retry" }
  /api/v1/idv-check:
    get:
      operationId: checkDirectorIdVerification
      x-credits: 1
      summary: Directors' and PSCs' ID verification for one UK company
      description: Every director and PSC, whether a verified identity shows on the Companies House register, and each person's own deadline from the register (a director's comes with the company's next confirmation statement; a PSC who isn't a director verifies in the first 14 days of their birth month; the last of them falls on 1 December 2026). 1 credit.
      security:
        - ApiKeyAuth: []
      parameters:
        - name: company
          in: query
          required: true
          description: A company number (e.g. 01234567, SC123456) or a company name
          schema: { type: string, maxLength: 200 }
      responses:
        "200": { description: "business, deadline, summary (total / verified / notShowing) and people" }
        "400": { description: "Missing company" }
        "401": { description: "Missing or wrong x-api-key" }
        "429": { description: "Monthly quota reached OR burst brake (60/min)" }
    post:
      operationId: checkDirectorIdVerificationBatch
      x-credits: "1 per company in the batch"
      summary: Directors' and PSCs' ID verification for up to 50 companies
      description: One credit per company. Returns each company's read plus the count of companies with people not showing on the register. Companies the register couldn't (fully) give us are credited back, and creditsRefunded says how many.
      security:
        - ApiKeyAuth: []
      requestBody:
        content:
          application/json:
            schema:
              type: object
              required: [companies]
              properties:
                companies:
                  type: array
                  maxItems: 50
                  items: { type: string, description: "A company number" }
      responses:
        "200": { description: "checked, companiesWithPeopleNotShowing, results[]" }
        "400": { description: "No valid company numbers, or more than 50" }
        "401": { description: "Missing or wrong x-api-key" }
        "429": { description: "Monthly quota reached OR burst brake (60/min)" }
  /api/v1/public-record:
    get:
      operationId: getCompanyPublicRecord
      x-credits: 3
      summary: Everything the public record says about a UK company, joined and read
      description: Registry facts, distress signals, directors' and PSCs' ID verification against the deadline, and (for large companies) the payment-practices record, with the three things most worth knowing first. Also public contracts won, the gender pay gap, Environment Agency registrations and a UK Sanctions List check. 3 credits.
      security:
        - ApiKeyAuth: []
      parameters:
        - { name: company, in: query, required: true, description: "A company number, or a name (resolved to one company, named in the reply)", schema: { type: string, maxLength: 200 } }
      responses:
        "200": { description: "company, level, knowFirst[], distress, identityVerification, paymentPractices" }
        "400": { description: "Missing company" }
        "401": { description: "Missing or wrong x-api-key" }
        "404": { description: "No registered company found" }
  /api/v1/watch:
    get:
      operationId: listWatches
      x-credits: 0
      summary: The companies this key watches, with the last state seen
      description: Free. Each watch carries the state last read (status, filings due, counts of directors and PSCs, ID verification still open, distress level, sanctions, latest payment report, awards, sponsor licence) and whether it's paused.
      security:
        - ApiKeyAuth: []
      responses:
        "200": { description: "watching, limit, creditsPerCompanyPerMonth, watches[]" }
        "401": { description: "Missing or wrong x-api-key" }
        "403": { description: "Not a customer key" }
    post:
      operationId: watchCompany
      x-credits: "2 per company per calendar month (charged on the first watch of the month)"
      summary: Watch a company
      description: Reads the company today and records its state; from tomorrow's daily read on, every change is an event. 2 credits now (this month), then 2 per calendar month on the first read of the month. A key out of credits has its watches paused, with an event saying so, until credits are there. Each plan watches up to a limit (free 10; paid a quarter of the monthly credits).
      security:
        - ApiKeyAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [company]
              properties:
                company: { type: string, description: "A Companies House number" }
                label: { type: string, maxLength: 120 }
      responses:
        "200": { description: "company, name, watching, label, state, creditsCharged" }
        "400": { description: "Not a company number" }
        "402": { description: "The plan's watch limit is reached (code watch_limit)" }
        "404": { description: "No company with that number" }
        "503": { description: "Companies House couldn't be read; nothing charged" }
    delete:
      operationId: unwatchCompany
      x-credits: 0
      summary: Stop watching a company
      security:
        - ApiKeyAuth: []
      parameters:
        - { name: company, in: query, required: true, schema: { type: string } }
      responses:
        "200": { description: "company, watching: false" }
        "404": { description: "Not being watched" }
  /api/v1/watch/events:
    get:
      operationId: watchEvents
      x-credits: 0
      summary: The watch's event feed
      description: Free. Oldest first after `since` (an event id); keep the last id you saw. Kinds include status, name, address, sic, accounts_overdue, accounts_filed, accounts_due, confirmation_overdue, confirmation_filed, officers, pscs, idv, distress, distress_signal, sanctions, sanctions_cleared, payment_report, contract_award, sponsor_added, sponsor_removed, sponsor_changed, not_found, watch_paused, watch_resumed.
      security:
        - ApiKeyAuth: []
      parameters:
        - { name: since, in: query, required: false, schema: { type: integer } }
        - { name: company, in: query, required: false, schema: { type: string } }
        - { name: limit, in: query, required: false, schema: { type: integer, minimum: 1, maximum: 500, default: 100 } }
      responses:
        "200": { description: "events[] {id, watchId, company, seenAt, kind, summary, before, after, deliveredToWebhook}, lastId, more" }
  /api/v1/watch/webhook:
    put:
      operationId: setWatchWebhook
      x-credits: 0
      summary: Where events are posted
      description: >-
        Free. A public https address. The signing secret comes back once. Each delivery is a JSON body of the
        form { "events": [ ... ] } with the header X-Katalis-Signature "t=<unix seconds>,v1=<hex HMAC-SHA256 of
        '<t>.<raw body>' with the secret>"; answer 2xx within 10 seconds, or it's retried five times over two days.
      security:
        - ApiKeyAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema: { type: object, required: [url], properties: { url: { type: string, format: uri } } }
      responses:
        "200": { description: "url, active, secret (once), verify (how to check the signature, with a worked example)" }
        "400": { description: "Not a public https address" }
    get:
      operationId: getWatchWebhook
      x-credits: 0
      summary: The webhook on this key
      security:
        - ApiKeyAuth: []
      responses:
        "200": { description: "url, active, recentFailures, since" }
    delete:
      operationId: deleteWatchWebhook
      x-credits: 0
      summary: Switch the webhook off (the feed keeps everything)
      security:
        - ApiKeyAuth: []
      responses:
        "200": { description: "active: false" }
  /api/v1/ownership:
    get:
      operationId: ownershipChain
      x-credits: 2
      summary: Who controls the company, followed up the register
      description: The people-with-significant-control chain followed company by company while the controller is a UK-registered company (up to five levels) - each company's name, number, status and insolvency flag, what each controller holds, where the register ends (a foreign parent, a listed company, a PSC statement), and any trouble above. Individuals counted, never named. 2 credits.
      security:
        - ApiKeyAuth: []
      parameters:
        - { name: company, in: query, required: true, description: "A company number or name", schema: { type: string, maxLength: 200 } }
      responses:
        "200": { description: "company, levels, ultimateParentOnRegister, registerEndsAt, chain[] {level, name, number, status, insolvencyHistory, controllers[], statement?}, troubleAbove[], reading" }
        "400": { description: "Missing company" }
        "401": { description: "Missing or wrong x-api-key" }
        "404": { description: "No registered company found" }
        "503": { description: "Companies House couldn't be read; nothing charged" }
  /api/v1/crp-check:
    get:
      operationId: carbonReductionPlanCheck
      x-credits: 4
      summary: A published Carbon Reduction Plan read against the PPN 006 template
      description: Given the plan's address (page or PDF) or the supplier's homepage (the link is found there), reads the plan and says which of the template's eight parts are there - net zero by 2050, baseline and current-year tCO2e, Scope 1 and 2, Scope 3 categories 4/5/6/7/9, reduction projects, director sign-off, updated within a year - with notes, the Scope 3 categories found and the gaps. Form, never the truth of the figures. 4 credits; a page with no plan is not charged.
      security:
        - ApiKeyAuth: []
      parameters:
        - { name: url, in: query, required: true, description: "The plan, or the supplier's homepage", schema: { type: string, maxLength: 500 } }
      responses:
        "200": { description: "url, foundFrom?, found, company, planDate, meetsTemplate, partsMet, partsTotal, parts {…}, scope3Found[], gaps[], reading, read" }
        "400": { description: "Missing or invalid url" }
        "401": { description: "Missing or wrong x-api-key" }
        "422": { description: "Couldn't read enough text; credits given back" }
  /api/v1/accounts:
    get:
      operationId: filedAccounts
      x-credits: 2
      summary: The company's latest filed accounts read into plain figures, this year and last
      description: From Companies House's tagged (iXBRL) accounts document - net assets, current assets, cash, debtors, creditors due within and after a year, equity, turnover and profit where the accounts include them, average employees, for the current and prior period; audit status, standards, dormant flag; what the accounts leave out. Accounts filed as a PDF can't be read and the call's credits go back. 2 credits.
      security:
        - ApiKeyAuth: []
      parameters:
        - { name: company, in: query, required: true, description: "A company number or name", schema: { type: string, maxLength: 200 } }
      responses:
        "200": { description: "company, filed, readable, accounts {filedOn, madeUpTo, kind, period, priorPeriod, standards, audited, auditExempt, dormant}, figures {name: {current, prior}}, ratios, notIncluded[], reading; or readable:false with creditsRefunded" }
        "400": { description: "Missing company" }
        "401": { description: "Missing or wrong x-api-key" }
        "404": { description: "No registered company found" }
        "503": { description: "Companies House couldn't be read; nothing charged" }
  /api/v1/ppn-018:
    get:
      operationId: ppn018PaymentCheck
      x-credits: 1
      summary: A company's statutory payment reports against the PPN 018 tests and the Fair Payment Code tiers
      description: The last two Payment Practices reports read against PPN 018's tests (at least 95% of invoices paid within 60 days, 90% with an action plan; all invoices paid within an average of 45 days) and the Fair Payment Code's percentage tiers (Gold 95% within 30 days; Bronze 95% within 60; Silver can't be judged from the report). A company that doesn't report is said so. 1 credit.
      security:
        - ApiKeyAuth: []
      parameters:
        - { name: company, in: query, required: true, description: "A company number or name", schema: { type: string, maxLength: 200 } }
      responses:
        "200": { description: "company, reported, ppn018 {latest, previous?, summary}, fairPaymentCode {gold, silver, bronze, note}, reportFigures, official, otherMatches[]?" }
        "400": { description: "Missing company" }
        "401": { description: "Missing or wrong x-api-key" }
  /api/v1/charges:
    get:
      operationId: securedBorrowing
      x-credits: 1
      summary: A company's registered charges in plain English
      description: From the Companies House register of charges - total and outstanding, the lenders (individuals never named), what each secures and over what, fixed or floating, a floating charge over everything, and the latest creation date. 1 credit.
      security:
        - ApiKeyAuth: []
      parameters:
        - { name: company, in: query, required: true, description: "A company number or name", schema: { type: string, maxLength: 200 } }
      responses:
        "200": { description: "company, summary {total, outstanding, partSatisfied, satisfied, lenders[], latestCreated, floatingChargeOverEverything}, reading, outstanding[], satisfiedCharges[], official" }
        "400": { description: "Missing company" }
        "401": { description: "Missing or wrong x-api-key" }
        "404": { description: "No registered company found" }
        "503": { description: "Companies House couldn't be read; nothing charged" }
  /api/v1/aml-register:
    get:
      operationId: checkAmlRegister
      x-credits: 1
      summary: Is a business on HMRC's Money Laundering Regulations supervised business register, for what, and since when
      description: Matched on the business's name as the register writes it (a company number brings its registered and former names), with the register's postcode district checked against the registered office. Current registrations with sectors and status (Approved, Applied, Expired), former ones with the date they went, and every change recorded for the name since 7 October 2026. HMRC is one supervisor among several, so absence never means unsupervised. People never loaded. 1 credit.
      security:
        - ApiKeyAuth: []
      parameters:
        - { name: company, in: query, required: true, description: "A company number, or the business's name", schema: { type: string, maxLength: 200 } }
      responses:
        "200": { description: "query, company?, matchedOn[], supervisedByHmrc, summary, entries[] {mlrNumber, name, businessName, tradingName, postcodeDistrict, status, sectors[], registeredSince, seenSince, lastSeen, districtMatchesRegisteredOffice?}, formerEntries[]?, history[], registerDate, historySince" }
        "400": { description: "Missing company" }
        "401": { description: "Missing or wrong x-api-key" }
        "404": { description: "No company with that number" }
        "503": { description: "Companies House couldn't be read; nothing charged" }
  /api/v1/aml-register/changes:
    get:
      operationId: listAmlChanges
      x-credits: 1
      summary: Every change on HMRC's supervised business register since a date
      description: Businesses added, removed, returned or whose status changed, newest first, from our diff of each new file (kept since 7 October 2026). 1 credit a page of up to 1,000.
      security:
        - ApiKeyAuth: []
      parameters:
        - { name: since, in: query, required: false, description: "YYYY-MM-DD (default: thirty days ago)", schema: { type: string } }
        - { name: change, in: query, required: false, schema: { type: string, enum: [added, removed, returned, status] } }
        - { name: limit, in: query, required: false, schema: { type: integer, maximum: 1000, default: 200 } }
        - { name: offset, in: query, required: false, schema: { type: integer, default: 0 } }
      responses:
        "200": { description: "since, registerDate, total, offset, changes[] {seen_on, change, mlr_number, name, postcode_district, before, after}" }
        "400": { description: "A bad date or filter" }
        "401": { description: "Missing or wrong x-api-key" }
  /api/v1/charity-check:
    get:
      operationId: checkCharity
      x-credits: 1
      summary: Is this a registered charity, is it in good standing, and what do its accounts say
      description: Read live from the Charity Commission for England and Wales' register, by charity number, by company number (matched through the Companies House number the register holds) or by name (up to three matches, registered first). Registered or removed with dates and reason; CIO or charitable company; insolvency, administration and interim-manager flags; reporting status; latest accounts and up to six years of income and spend broken down; what it does, for whom and where; trustee count and chair; governing document, objects, other regulators. England and Wales only. Trustees counted, never named. Contains information from the Charity Commission, Open Government Licence v3.0. 1 credit.
      security:
        - ApiKeyAuth: []
      parameters:
        - { name: company, in: query, required: true, description: "A charity number (263710, or 1053467-2 for a group member), a company number, or a charity's name", schema: { type: string, maxLength: 200 } }
      responses:
        "200": { description: "query, company?, matchedBy, registered, summary, charities[] {charityNumber, groupSuffix, name, otherNames[], status, statusCode, registered, registeredOn, removedOn, removalReason, type, cio, companyNumber, flags {insolvent, inAdministration, interimManager, interimManagerSince}, reportingStatus, latestAccounts {yearStart, yearEnd, income, expenditure}, financialHistory[] {yearEnd, income, expenditure, consolidated, incomeFromGovernmentContracts, incomeFromGovernmentGrants, donationsAndLegacies, charitableActivitiesIncome, otherTrading, investmentIncome, charitableActivitiesSpend, raisingFunds, governance, grantsToInstitutions}, classification {what[], who[], how[]}, areasOfOperation[], trustees {count, hasChair, latestAppointment}, governingDocument, objects, areaOfBenefit, otherRegulators[], address, website, lastModified, registerEntry}, note, attribution" }
        "400": { description: "Missing company" }
        "401": { description: "Missing or wrong x-api-key" }
        "404": { description: "No company with that number" }
        "503": { description: "Companies House or the Charity Commission couldn't be read; nothing charged" }
  /api/v1/cqc-check:
    get:
      operationId: checkCqcProvider
      x-credits: 1
      summary: Is this company a CQC-registered care provider, how are its services rated, and has that changed
      description: Matched on our daily copy of the Care Quality Commission's register of providers (the CQC's own Companies House number, then the company's registered and former names, or a CQC provider or location id); the provider and up to twelve of its registered locations are then read live from the CQC. Registered or deregistered with dates; the kind of provider; every location (care homes, agencies, surgeries, clinics) with its overall rating, the five key questions, inspection and report dates; every change recorded for it since 2 October 2026. England only. Organisations and services only, never people. Contains CQC information licensed under the Open Government Licence v3.0. 1 credit.
      security:
        - ApiKeyAuth: []
      parameters:
        - { name: company, in: query, required: true, description: "A company number, a provider's name, or a CQC provider or location id (1-…)", schema: { type: string, maxLength: 200 } }
      responses:
        "200": { description: "query, company?, matchedBy, registered, summary, providers[] {cqcId, name, registrationStatus, registered, registeredOn, deregisteredOn, type, ownershipType, directorate, companyNumber, charityNumber, brand, address, website, regulatedActivities[], locationsCount, locationsSummary {registered, deregistered, byRating, notYetRated, worstRating, words}, locations[] {cqcId, name, registered, careHome, beds, postcode, localAuthority, overallRating, ratedOn, framework ('key questions (to 2024)' | 'single assessment (from 2024)'), keyQuestions{safe, effective, caring, responsive, wellled} (the old framework), serviceRatings[] {service, rating, assessedOn, status} (the new one), previousRatings[], lastInspectedOn, serviceTypes[], specialisms[], cqcPage}, cqcPage}, location?, history[], historySince, note, attribution" }
        "400": { description: "Missing company" }
        "401": { description: "Missing or wrong x-api-key" }
        "404": { description: "No company with that number" }
        "503": { description: "Companies House couldn't be read; nothing charged" }
  /api/v1/cqc-check/changes:
    get:
      operationId: listCqcChanges
      x-credits: 1
      summary: Every change on the CQC's register since a date
      description: Providers and locations added, deregistered or re-registered (status), or whose overall rating changed (rating), newest first, from our daily sync of the CQC's own changes feed (kept since 2 October 2026). Filter to the rating or status the change went to, e.g. to=Inadequate. 1 credit a page of up to 1,000.
      security:
        - ApiKeyAuth: []
      parameters:
        - { name: since, in: query, required: false, description: "YYYY-MM-DD (default: thirty days ago)", schema: { type: string } }
        - { name: change, in: query, required: false, schema: { type: string, enum: [added, status, rating] } }
        - { name: kind, in: query, required: false, schema: { type: string, enum: [provider, location] } }
        - { name: to, in: query, required: false, description: "The rating or status the change went to: Outstanding, Good, Requires improvement, Inadequate, Registered, Deregistered", schema: { type: string } }
        - { name: limit, in: query, required: false, schema: { type: integer, maximum: 1000, default: 200 } }
        - { name: offset, in: query, required: false, schema: { type: integer, default: 0 } }
      responses:
        "200": { description: "since, total, offset, changes[] {seenOn, change, kind, cqcId, providerId, name, providerName, companyNumber, before, after, cqcPage}, note, attribution" }
        "400": { description: "A bad date or filter" }
        "401": { description: "Missing or wrong x-api-key" }
  /api/v1/sra-check:
    get:
      operationId: checkSraFirm
      x-credits: 1
      summary: Is this an SRA-authorised law firm, what kind, since when, and has that changed
      description: From our daily copy of the Solicitors Regulation Authority's register of regulated organisations. Matched on the register's own Companies House number, then the company's registered and former names, or by SRA number. Authorised, authorised with conditions, ceased, revoked or intervened, with dates; the kind of firm; work areas, offices and head office; trading and former names; every change recorded for the firm since 8 October 2026. Firms only, never individuals. Data supplied by the Solicitors Regulation Authority. 1 credit.
      security:
        - ApiKeyAuth: []
      parameters:
        - { name: company, in: query, required: true, description: "A company number, a firm's name, or an SRA number", schema: { type: string, maxLength: 200 } }
      responses:
        "200": { description: "query, company?, matchedBy, authorised, summary, firms[] {sraNumber, name, companyNumber, authorised, status, statusCode, statusSince, authorisedSince, kind, kindCode, constitution, offices, headOffice, tradingNames[], previousNames[], workAreas[], reservedActivities[], websites[], seenSince, lastSeen, sraRegisterEntry}, formerEntries[]?, history[], historySince, note, attribution" }
        "400": { description: "Missing company" }
        "401": { description: "Missing or wrong x-api-key" }
        "404": { description: "No company with that number" }
        "503": { description: "Companies House couldn't be read; nothing charged" }
  /api/v1/sra-check/changes:
    get:
      operationId: listSraChanges
      x-credits: 1
      summary: Every change on the SRA's register of law firms since a date
      description: Firms added, removed, returned or whose authorisation status changed (authorised to revoked, intervened, ceased, conditions), newest first, from our daily diff (kept since 8 October 2026). 1 credit a page of up to 1,000.
      security:
        - ApiKeyAuth: []
      parameters:
        - { name: since, in: query, required: false, description: "YYYY-MM-DD (default: thirty days ago)", schema: { type: string } }
        - { name: change, in: query, required: false, schema: { type: string, enum: [added, removed, returned, status] } }
        - { name: limit, in: query, required: false, schema: { type: integer, maximum: 1000, default: 200 } }
        - { name: offset, in: query, required: false, schema: { type: integer, default: 0 } }
      responses:
        "200": { description: "since, total, offset, changes[] {seenOn, change, sraNumber, name, companyNumber, before, after, beforeWords, afterWords}, note, attribution" }
        "400": { description: "A bad date or filter" }
        "401": { description: "Missing or wrong x-api-key" }
  /api/v1/late-payment:
    get:
      operationId: latePaymentSums
      x-credits: 1
      summary: Statutory interest and fixed compensation on a late business invoice
      description: Under the Late Payment of Commercial Debts (Interest) Act 1998, interest at 8% over the Bank Rate on the previous 30 June or 31 December (each half-year at its own rate), simple, by the day, from the day after the due date to the paid date (today if unpaid), plus the fixed compensation (£40 / £70 / £100 by the invoice's size). The Bank Rate is read from the Bank of England's page daily. 1 credit.
      security:
        - ApiKeyAuth: []
      parameters:
        - { name: amount, in: query, required: true, description: "The invoice amount in pounds", schema: { type: number } }
        - { name: due, in: query, required: true, description: "The date payment was due (YYYY-MM-DD)", schema: { type: string } }
        - { name: paid, in: query, required: false, description: "The date it was paid; leave out if unpaid", schema: { type: string } }
      responses:
        "200": { description: "invoice, late, daysLate, statutoryInterest, fixedCompensation, total, rate {pct, bankRatePct, basis}, periods[], perDay, currentStatutoryRatePct, reading, bankRateReadAt" }
        "400": { description: "A bad amount or date" }
        "401": { description: "Missing or wrong x-api-key" }
        "503": { description: "The Bank's rate page couldn't be read; nothing charged" }
  /api/v1/will-they-pay:
    get:
      operationId: willTheyPay
      x-credits: 1
      summary: A large customer's payment habits from the supplier's side, with what its lateness is worth on your invoice
      description: From the company's statutory Payment Practices reports - typical days to pay against your terms, the share paid late and past 60 days, the trend over its last reports - and the statutory interest plus fixed compensation its typical lateness is worth on an invoice of your size. A company that doesn't report is said so. 1 credit.
      security:
        - ApiKeyAuth: []
      parameters:
        - { name: company, in: query, required: true, description: "A company number or name", schema: { type: string, maxLength: 200 } }
        - { name: invoice, in: query, required: false, description: "Your invoice amount in pounds (default 1000)", schema: { type: number } }
        - { name: terms, in: query, required: false, description: "Your payment terms in days (default 30)", schema: { type: integer } }
      responses:
        "200": { description: "company, reported, yourInvoice, typically {daysToPay, daysLateAgainstYourTerms, paidLateAgainstTermsPct, paidAfter60DaysPct, paidWithin30DaysPct}, trend, worthToYou, latestReport, reading, forSuppliers[], otherMatches[]?" }
        "400": { description: "Missing company or a bad invoice/terms" }
        "401": { description: "Missing or wrong x-api-key" }
  /api/v1/sponsor-licence:
    get:
      operationId: checkSponsorLicence
      x-credits: 1
      summary: Is an organisation on the register of licensed sponsors (workers), on which routes and at what rating
      description: Matched on the organisation's name as the register writes it (a company number brings its registered and former names), with the register's town checked against the registered office. Current licences, former licences with the date they went, and every change recorded for the name since 6 October 2026. 1 credit.
      security:
        - ApiKeyAuth: []
      parameters:
        - { name: company, in: query, required: true, description: "A company number, or the organisation's name", schema: { type: string, maxLength: 200 } }
      responses:
        "200": { description: "query, company?, matchedOn[], licensed, summary, licences[] {organisation, town, county, type, rating, route, seenSince, lastSeen, townMatchesRegisteredOffice?}, formerLicences[]?, history[] {seen_on, change, name, town, route, before, after}, registerDate, historySince" }
        "400": { description: "Missing company" }
        "401": { description: "Missing or wrong x-api-key" }
        "404": { description: "No company with that number" }
        "503": { description: "Companies House couldn't be read; nothing charged" }
  /api/v1/sponsor-licence/changes:
    get:
      operationId: listSponsorChanges
      x-credits: 1
      summary: Every change on the register of licensed sponsors since a date
      description: Sponsors added, removed, returned or re-rated, newest first, from our daily diff of the register (kept since 6 October 2026). 1 credit a page of up to 1,000.
      security:
        - ApiKeyAuth: []
      parameters:
        - { name: since, in: query, required: false, description: "YYYY-MM-DD (default: seven days ago)", schema: { type: string } }
        - { name: change, in: query, required: false, schema: { type: string, enum: [added, removed, returned, rating] } }
        - { name: route, in: query, required: false, description: "Part of a route name, e.g. Skilled Worker", schema: { type: string } }
        - { name: limit, in: query, required: false, schema: { type: integer, maximum: 1000, default: 200 } }
        - { name: offset, in: query, required: false, schema: { type: integer, default: 0 } }
      responses:
        "200": { description: "since, registerDate, total, offset, changes[] {seen_on, change, name, town, route, before, after}" }
        "400": { description: "A bad date or filter" }
        "401": { description: "Missing or wrong x-api-key" }
  /api/v1/website-legal:
    get:
      operationId: checkWebsiteLegal
      x-credits: 4
      summary: The legal basics a UK business website must have, and who's behind the site
      description: Reads the homepage and its linked legal and contact pages, visits the site in a real browser (nothing clicked) to see which advertising or tracking cookies land before consent, matches the company details shown to Companies House, checks the privacy notice covers the basics, the ICO fee register, and the contact details the E-Commerce Regulations ask for. Each check carries its result (pass, warn, fail), what we saw, the rule and what to do. "behind" is the registered company the site states or confirms (confirmed true) or the best name match (confirmed false). 4 credits; a site that couldn't be read is not charged.
      security:
        - ApiKeyAuth: []
      parameters:
        - { name: url, in: query, required: true, description: "The website (a domain or URL)", schema: { type: string, maxLength: 500 } }
        - { name: company, in: query, required: false, description: "The company number, if you know it", schema: { type: string } }
      responses:
        "200": { description: "url, behind {name, number, status, how, confirmed, note?, alternatives?}, registrationsStated?, legal {score|null, checks[] {code, result, found, rule, advice?}, couldntCheck[], notApplicable[] {code, reason}}, pagesRead[]" }
        "400": { description: "Missing or invalid url, or a page on a platform" }
        "401": { description: "Missing or wrong x-api-key" }
        "422": { description: "The site couldn't be read; credits given back" }
  /api/v1/deadlines:
    get:
      operationId: getCompanyDeadlines
      x-credits: 1
      summary: Every deadline a UK company has, sorted, with days left
      description: Accounts and confirmation statement (Companies House), each director's and PSC's identity-verification date as counts (the earliest among those not yet showing as verified), the ICO data protection fee renewal (the fee register entry matching the company's name), and the next payment practices and gender pay gap reports where the company reports them. Overdue first. The reply carries the address of a free calendar feed for the company. 1 credit.
      security:
        - ApiKeyAuth: []
      parameters:
        - { name: company, in: query, required: true, description: "A company number, or a name", schema: { type: string, maxLength: 200 } }
      responses:
        "200": { description: "company, deadlines[] {kind, title, due, daysLeft, status (overdue|due_soon|upcoming|ok|check), detail, source, official}, next, couldntRead[], calendar {url}" }
        "400": { description: "Missing company" }
        "401": { description: "Missing or wrong x-api-key" }
        "404": { description: "No registered company found" }
        "503": { description: "Companies House couldn't be read; nothing charged" }
  /api/v1/deadlines.ics:
    get:
      operationId: getCompanyDeadlinesCalendar
      x-credits: 0
      summary: The company's deadlines as a calendar feed (free)
      description: text/calendar, one all-day event per dated deadline, refreshed at most twice a day. The token is the key's own calendar token, given in GET /api/v1/deadlines; it opens only calendars.
      parameters:
        - { name: company, in: query, required: true, schema: { type: string } }
        - { name: t, in: query, required: true, description: "The calendar token", schema: { type: string } }
      responses:
        "200": { description: "An iCalendar feed", content: { "text/calendar": { schema: { type: string } } } }
        "404": { description: "Token no longer valid, or no such company" }
        "429": { description: "More than 60 fetches an hour" }
  /api/v1/contracts-won:
    get:
      operationId: getPublicContractsWon
      x-credits: 1
      summary: Public-sector contracts a UK company has been awarded
      description: From Contracts Finder and Find a Tender award notices — count, notified value (contracts and framework ceilings apart; `contracts.withoutValue` counts awards with no published value), top buyers and the latest awards (the lots of one notice as one line with `lots`; a notice published on both feeds once, the other under `alsoNotified`). Matched by company number where a notice gives one (both feeds' numbers read since 10 Oct 2026), else by exact registered name. 1 credit.
      security:
        - ApiKeyAuth: []
      parameters:
        - { name: company, in: query, required: true, description: "A company number or name", schema: { type: string, maxLength: 200 } }
        - { name: months, in: query, required: false, schema: { type: integer, minimum: 1, maximum: 60 } }
      responses:
        "200": { description: "company, awards, contracts { count, notifiedValueGBP, withoutValue }, frameworks { count, ceilingValueGBP }, topBuyers[], reading, latest[] (lots?, alsoNotified?)" }
        "400": { description: "Missing company" }
        "401": { description: "Missing or wrong x-api-key" }
        "404": { description: "No registered company found" }
  /api/v1/gender-pay-gap:
    get:
      operationId: getGenderPayGap
      x-credits: 1
      summary: An employer's gender pay gap reports, read and benchmarked
      description: Median and mean gaps, bonus gaps, women by pay quarter, year-on-year trend and rank against all reporters and the sector. 1 credit.
      security:
        - ApiKeyAuth: []
      parameters:
        - { name: company, in: query, required: true, description: "A company number or employer name", schema: { type: string, maxLength: 200 } }
      responses:
        "200": { description: "employer, latest, trend, history[], benchmark, reading — or reported:false" }
        "400": { description: "Missing company" }
        "401": { description: "Missing or wrong x-api-key" }
  /api/v1/registers:
    get:
      operationId: checkLicencesAndRegisters
      x-credits: 2
      summary: Environment Agency registrations and a UK Sanctions List check for one company
      description: Waste carrier / broker / dealer status and expiry, permits, exemptions and enforcement actions (England), plus whether the company's number, name or former names match an entity on the UK Sanctions List (a lead to confirm, never a finding). 2 credits.
      security:
        - ApiKeyAuth: []
      parameters:
        - { name: company, in: query, required: true, description: "A company number or name", schema: { type: string, maxLength: 200 } }
      responses:
        "200": { description: "company, reading, environmentAgency, sanctions" }
        "400": { description: "Missing company" }
        "401": { description: "Missing or wrong x-api-key" }
        "404": { description: "No registered company found" }
  /api/v1/art:
    get:
      operationId: drawArt
      x-credits: 1
      summary: Generative art in your colours, as an SVG
      description: One of 22 styles (Mondrian, Bauhaus, Topographic, Voronoi, Halftone, Isometric…) drawn from 2–6 hex colours. Deterministic by seed. Our own code, no image model. 1 credit; ?list=1 (the styles and palettes) is free.
      security:
        - ApiKeyAuth: []
      parameters:
        - { name: style, in: query, required: true, description: "One of the styles from ?list=1", schema: { type: string } }
        - { name: colors, in: query, required: false, description: "2–6 hex colours, comma-separated, e.g. 1f6fe5,57dbaa", schema: { type: string } }
        - { name: palette, in: query, required: false, description: "A named palette from ?list=1 (used when colors is absent)", schema: { type: string } }
        - { name: seed, in: query, required: false, description: "Same seed, same image", schema: { type: string, maxLength: 120 } }
        - { name: width, in: query, required: false, schema: { type: integer, minimum: 100, maximum: 4000, default: 1600 } }
        - { name: height, in: query, required: false, schema: { type: integer, minimum: 100, maximum: 4000, default: 900 } }
        - { name: list, in: query, required: false, description: "Set to 1 for the style and palette names", schema: { type: string } }
      responses:
        "200": { description: "image/svg+xml (or JSON styles/palettes with list=1)" }
        "400": { description: "Unknown style" }
        "401": { description: "Missing or wrong x-api-key" }
  /api/v1/brand-kit:
    get:
      operationId: readBrandKit
      x-credits: 2
      summary: A website's brand, read from the site itself
      description: Colours, fonts (with the nearest Google Fonts pairing), logo, tone of voice with what the copy leans on and avoids, main call to action, About line, and the site's own photos. Unreadable parts come back empty. 2 credits.
      security:
        - ApiKeyAuth: []
      parameters:
        - { name: website, in: query, required: true, description: "Any public website, e.g. example.co.uk", schema: { type: string, maxLength: 300 } }
      responses:
        "200": { description: "name, colors[], logo, fonts, voice, callToAction, about, photos[]" }
        "400": { description: "Missing website" }
        "401": { description: "Missing or wrong x-api-key" }
        "422": { description: "The site couldn't be opened" }
  /api/v1/effects:
    get:
      operationId: listEffectEmbeds
      x-credits: 0
      summary: The effects on offer (?list=1, free, no key) or your embeds with views this month (with a key)
      parameters:
        - { name: list, in: query, required: false, schema: { type: string } }
      responses:
        "200": { description: "effects[] or embeds[]" }
    post:
      operationId: createEffectEmbed
      x-credits: "3 per embed made; views 1 per 1,000 (lib/fx/apiEmbeds)"
      summary: Make an embed of one effect in your colours
      description: Returns a token and the snippet (one script tag plus an attribute on the section it goes behind). 3 credits, then 1 credit per 1,000 page views; when the credits run out the embed pauses until the allowance renews.
      security:
        - ApiKeyAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                effect: { type: string, description: "An id from ?list=1" }
                colors: { type: array, items: { type: string }, description: "Up to 4 hex colours" }
                credit: { type: boolean, description: "false removes the credit link (paid plans only)" }
                recipe: { type: object, description: "Your own effect recipe instead of an id (validated)" }
      responses:
        "201": { description: "token, effect, use, credit, snippet, preview" }
        "400": { description: "Unknown effect" }
        "401": { description: "Missing or wrong x-api-key" }
        "409": { description: "50 embeds already switched on for this key" }
    delete:
      operationId: stopEffectEmbed
      x-credits: 0
      summary: Switch an embed off (every copy of its snippet stops)
      security:
        - ApiKeyAuth: []
      parameters:
        - { name: token, in: query, required: true, schema: { type: string } }
      responses:
        "200": { description: "token, active: false" }
        "404": { description: "No embed with that token on this key" }
  /api/v1/tender-match:
    get:
      operationId: matchTenders
      x-credits: 3
      summary: Open Contracts Finder notices that fit a UK business
      description: Reads what the business does, scans open notices published in the last `days` (default 7, max 30), returns matches with a fit score and a reason. 3 credits.
      security:
        - ApiKeyAuth: []
      parameters:
        - { name: q, in: query, required: true, description: "A business name or website", schema: { type: string, maxLength: 200 } }
        - { name: days, in: query, required: false, schema: { type: integer, minimum: 1, maximum: 30 } }
      responses:
        "200": { description: "business, scanned, matches[] (title, buyer, deadline, fit, why)" }
        "400": { description: "Missing q" }
        "401": { description: "Missing or wrong x-api-key" }
  /api/v1/ai-visibility:
    get:
      operationId: checkAiVisibility
      x-credits: 5
      summary: Whether AI models know a UK business, asked cold and graded
      description: Each model's answer graded against the verified profile (knows / vague / unknown / confused), a 0–100 score and advice. 5 credits.
      security:
        - ApiKeyAuth: []
      parameters:
        - { name: q, in: query, required: true, description: "A business name or website", schema: { type: string, maxLength: 200 } }
      responses:
        "200": { description: "business, visibility { score, results[], advice[] }" }
        "400": { description: "Missing q" }
        "401": { description: "Missing or wrong x-api-key" }
  /api/v1/distress-signals:
    get:
      operationId: getCompanyDistressSignals
      x-credits: 1
      summary: What the Companies House register shows about a company's health
      description: Status, strike-off proposals, insolvency cases, overdue filings, recent charges and directors leaving — each graded serious / watch / info with a date, plus a one-line summary. Matched by company number only. 1 credit.
      security:
        - ApiKeyAuth: []
      parameters:
        - name: company
          in: query
          required: true
          description: A company number, or a name (resolved to one company, named in the reply)
          schema: { type: string, maxLength: 200 }
      responses:
        "200": { description: "company, level (clear | watch | serious | unread: part of the register couldn't be read just now, see partial[]), signals[], summary, partial[]" }
        "400": { description: "Missing company" }
        "401": { description: "Missing or wrong x-api-key" }
        "429": { description: "Monthly quota reached OR burst brake (60/min)" }
    post:
      operationId: getCompanyDistressSignalsBatch
      x-credits: "1 per company in the batch"
      summary: Distress signals for up to 50 companies
      security:
        - ApiKeyAuth: []
      requestBody:
        content:
          application/json:
            schema:
              type: object
              required: [companies]
              properties:
                companies: { type: array, maxItems: 50, items: { type: string } }
      responses:
        "200": { description: "checked, serious, watch, results[]" }
        "400": { description: "No valid company numbers, or more than 50" }
        "401": { description: "Missing or wrong x-api-key" }
  /api/v1/payment-practices:
    get:
      operationId: getSupplierPaymentPractices
      x-credits: 1
      summary: How fast a large UK company pays its suppliers
      description: From the company's statutory Payment Practices reports (OGL v3) — average days to pay, share paid late against agreed terms, terms, trend over its last reports, benchmark against all recent reporters, and a plain reading for suppliers. 1 credit. A name also returns otherMatches (other reporting companies it matches, by number).
      security:
        - ApiKeyAuth: []
      parameters:
        - name: company
          in: query
          required: true
          description: A company number or name
          schema: { type: string, maxLength: 200 }
      responses:
        "200": { description: "company, latest, trend, history, benchmark, reading, forSuppliers — or reported:false for companies that don't report" }
        "400": { description: "Missing company" }
        "401": { description: "Missing or wrong x-api-key" }
        "429": { description: "Monthly quota reached OR burst brake (60/min)" }
  /api/v1/company-logo:
    get:
      operationId: companyLogo
      x-credits: 0
      summary: Company logo lookup (free, no key)
      security: []
      description: >
        Returns a link to a company's own published logo asset for a domain, with a legal
        `notice` field (nominative/identification use only — trademarks remain their
        owners'). Keyless and free; IP rate-limited. The /api/v1/demo endpoint is likewise
        keyless (one enrichment per IP per day) and intentionally undocumented here.
      parameters:
        - name: domain
          in: query
          required: true
          description: The company's domain (e.g. greggs.co.uk).
          schema: { type: string, maxLength: 200 }
      responses:
        "200": { description: "Logo URL(s) + the legal notice field" }
        "400": { description: "Missing or invalid domain" }
        "429": { description: "IP rate limit — retry later" }
components:
  securitySchemes:
    ApiKeyAuth:
      type: apiKey
      in: header
      name: x-api-key
  schemas:
    Error:
      type: object
      description: Every error reply, from every route. The credits for the call are given back when the failure was ours (a 5xx, a timeout, a register we couldn't read); creditsRefunded says how many, and the x-credits-refunded header repeats it.
      required: [error, code, requestId]
      properties:
        error: { type: string, description: "What went wrong, in plain English" }
        code: { type: string, description: "A short machine-readable code, e.g. bad_request, unauthorised, not_found, register_unreachable, rate_limited, server_error, timeout" }
        requestId: { type: string, description: "Quote it to support; it's also the x-request-id header" }
        creditsRefunded: { type: integer, description: "Credits given back for this call, when any" }
    Signal:
      type: object
      properties:
        code: { type: string }
        label: { type: string }
        kind: { type: string, enum: [opportunity, risk, info] }
    Accuracy:
      type: object
      properties:
        score: { type: integer, example: 92 }
        confidence: { type: string, enum: [high, medium, low] }
        breakdown:
          type: array
          items:
            type: object
            properties:
              label: { type: string }
              points: { type: number }
              max: { type: number }
    BusinessProfile:
      type: object
      properties:
        found: { type: boolean }
        name: { type: string }
        website: { type: string }
        companyNumber: { type: string }
        status: { type: string }
        sicCodes: { type: array, items: { type: string } }
        incorporationDate: { type: string }
        sizeSignal: { type: string }
        whatTheyDo: { type: string }
        services: { type: array, items: { type: string } }
        sector: { type: string }
        signals: { type: array, items: { $ref: "#/components/schemas/Signal" } }
        accuracy: { $ref: "#/components/schemas/Accuracy" }
        confidence: { type: string, enum: [high, medium, low] }
        sources: { type: array, items: { type: string } }
        attribution: { type: string }
    EnrichResponse:
      type: object
      properties:
        ms: { type: integer }
        profile: { $ref: "#/components/schemas/BusinessProfile" }
