{"info":{"name":"einvoice-api API","description":"Auto-generated from the live OpenAPI document. Set the api_key variable to an ek_live_… key.","schema":"https://schema.getpostman.com/json/collection/v2.1.0/collection.json"},"variable":[{"key":"base_url","value":"https://www.facturproof.com"},{"key":"api_key","value":"ek_live_replace_me"}],"item":[{"name":"Health","request":{"method":"GET","header":[],"url":{"raw":"{{base_url}}/health","host":["{{base_url}}"],"path":["health"]},"description":""}},{"name":"Selfcheck","request":{"method":"GET","header":[],"url":{"raw":"{{base_url}}/internal/selfcheck","host":["{{base_url}}"],"path":["internal","selfcheck"]},"description":"Deploy hook gate: engines must be runnable before the image takes traffic.\n\nUnauthenticated on purpose — the deploy hook calls it before any key exists.\nIteration 5 adds two more booleans alongside the original engine pair:\nextract_selfcheck_ok (CII+UBL parse round-trip) and convert_selfcheck_ok\n(CII+UBL parse+map, plus a full build+self-check when the render stack is\nactually installed) — see app/services/selfcheck_probes.py for why each\nis safe to run on every poll (fixed samples, no network, the PDF build is\nthe same lru_cached call GET /v1/convert/try already makes)."}},{"name":"Mcp Endpoint","request":{"method":"POST","header":[],"url":{"raw":"{{base_url}}/mcp","host":["{{base_url}}"],"path":["mcp"]},"description":"MCP Streamable HTTP endpoint (blueprint layer 7).\n\nAuthentication is the same dependency every other authenticated route uses,\nso a bad key is a 401 here too — MCP is a door onto the product, not a way\naround its gate."}},{"name":"Consistency","request":{"method":"POST","header":[{"key":"X-API-Key","value":"{{api_key}}"},{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{base_url}}/v1/consistency","host":["{{base_url}}"],"path":["v1","consistency"]},"description":"Read an inbound e-invoice (hybrid PDF/A-3, CII XML, or UBL) and return its\nheader PLUS internal-consistency findings — the receive-side tamper / mis-issue\ncheck (1 credit, priced as extract). Paid-only; mirrors /v1/extract's money\npath (auth, size cap, credit hold, idempotent replay). See\napp/pipeline.run_consistency.","body":{"mode":"raw","raw":"{}","options":{"raw":{"language":"json"}}}}},{"name":"Consistency Try","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{base_url}}/v1/consistency/try","host":["{{base_url}}"],"path":["v1","consistency","try"]},"description":"No-key trial for CONSISTENCY — the tamper-check counterpart of\n/v1/extract/try. Shares the SAME IP+month trial pool (trial.consume /\ntrial.trial_key) as validate/try and extract/try: all three are anonymous\nreads drawing on one \"let a stranger see real output before signing up\"\nallowance, so trying several in a session spends one budget, not three.\n\nA parse failure (ExtractError) returns the trial — the same null rule\neverywhere else applies, here to a 422. check_consistency is pure Decimal\nmaths over the parsed header (no engine, no I/O), so a consistency trial\ncosts no more than an extract trial and adds no new failure mode.","body":{"mode":"raw","raw":"{}","options":{"raw":{"language":"json"}}}}},{"name":"Convert","request":{"method":"POST","header":[{"key":"X-API-Key","value":"{{api_key}}"},{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{base_url}}/v1/convert","host":["{{base_url}}"],"path":["v1","convert"]},"description":"Read an inbound e-invoice — hybrid PDF/A-3, raw CII XML, or a UBL\ninvoice — and RE-EMIT it as a compliant MINIMUM-profile Factur-X PDF\n(2 credits, same price as /v1/render: see app/pipeline.run_conversion for\nwhy — it is render's own build step, fed by a parse+map stage instead of\na caller's JSON body). See app/services/convert.py for why the output is\nalways Profile.MINIMUM.","body":{"mode":"raw","raw":"{}","options":{"raw":{"language":"json"}}}}},{"name":"Convert Try","request":{"method":"GET","header":[],"url":{"raw":"{{base_url}}/v1/convert/try","host":["{{base_url}}"],"path":["v1","convert","try"]},"description":"No-key trial for CONVERT — the convert counterpart of /v1/render/try.\n\nMirrors render/try EXACTLY, not extract/try: convert runs the same\nWeasyPrint + two-JVM build render pays 2 credits for (build_invoice_pdf),\nso a trial that accepted a caller's own upload would give that compute\naway for the price of a rate limit. Instead this converts a FIXED curated\nCII sample (app/render/convert_sample.py) through the REAL parse -> map ->\nrender pipeline, cached the same way (lru_cache — built once, byte-stable\nthereafter). Deliberately does NOT touch the validate/extract trial pool,\nfor the same reason render/try does not: a different, more expensive\nfunnel with a different meter."}},{"name":"Eudr Geojson Try","request":{"method":"POST","header":[],"url":{"raw":"{{base_url}}/v1/eudr/geojson/try","host":["{{base_url}}"],"path":["v1","eudr","geojson","try"]},"description":"No-key trial for EUDR geolocation PRE-FLIGHT — validate the GeoJSON an operator\nwill attach to a Due Diligence Statement (DDS) BEFORE submitting it to EU TRACES.\n\nPOST the GeoJSON as the raw JSON request body. Returns {valid, feature_count,\ngeometry_types, total_area_ha, findings[]} — the geometry contract TRACES enforces\n(FeatureCollection shape, WGS84 coordinate ranges, closed polygon rings) plus the\nregulation's point-vs-polygon rule (a Point only for a plot < 4 ha). Keyless and\nfree: the check is a LOCAL computation with no external call, so it is bounded by a\nper-IP rate limit (and the global body-size limit) — it does NOT burn the VIES trial\npool. The full DDS envelope (operator, HS codes, producer country) is a later layer:\nvalid:true here means the GEOMETRY will not be rejected, not that the whole statement\nis complete. Honest by construction — an unparseable body is a 422, never a fake pass."}},{"name":"Extract","request":{"method":"POST","header":[{"key":"X-API-Key","value":"{{api_key}}"},{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{base_url}}/v1/extract","host":["{{base_url}}"],"path":["v1","extract"]},"description":"Read a hybrid Factur-X/ZUGFeRD PDF/A-3, raw CII XML, or a UBL invoice into\nheader JSON (1 credit) — mirrors /v1/validate exactly (auth, size cap,\ncredit hold, idempotent replay); see app/pipeline.run_extraction.","body":{"mode":"raw","raw":"{}","options":{"raw":{"language":"json"}}}}},{"name":"Extract Try","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{base_url}}/v1/extract/try","host":["{{base_url}}"],"path":["v1","extract","try"]},"description":"No-key trial for EXTRACT — the extract counterpart of /v1/validate/try.\n\nDeliberately shares validate/try's trial pool (same `trial.consume`/\n`trial.trial_key`, keyed on IP+month only, not on endpoint): both are read\nendpoints against the same \"let a stranger see real output before signing\nup\" allowance, so a caller trying both in the same session draws down one\nbudget rather than getting two. render/try is the one exception, and stays\none, for the reason already given there (a fixed sample, different funnel).\n\nOutside the credit path like validate/try: there is no account, so nothing\nto reserve/settle/record. A parse failure (ExtractError) gives the trial\nback — same null rule as everywhere else, applied to a 422 instead of a\nnull verdict.","body":{"mode":"raw","raw":"{}","options":{"raw":{"language":"json"}}}}},{"name":"Healthz","request":{"method":"GET","header":[],"url":{"raw":"{{base_url}}/v1/healthz","host":["{{base_url}}"],"path":["v1","healthz"]},"description":"Public engine-availability matrix (blueprint route table).\n\nThe null rule applied to the health surface: an engine whose binary is not\nrunnable reports available=false and the service reports \"degraded\". Saying\notherwise would be the same fabrication as a fake pass — and this is the\nendpoint an integrator checks before trusting a verdict.\n\nDistinct from /internal/selfcheck, which stays the deploy hook's gate and\nreturns the two raw booleans it was written against."}},{"name":"Me","request":{"method":"GET","header":[{"key":"X-API-Key","value":"{{api_key}}"}],"url":{"raw":"{{base_url}}/v1/me","host":["{{base_url}}"],"path":["v1","me"]},"description":"Entitlement introspection. Free — it moves no credits."}},{"name":"Render","request":{"method":"POST","header":[{"key":"X-API-Key","value":"{{api_key}}"},{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{base_url}}/v1/render","host":["{{base_url}}"],"path":["v1","render"]},"description":"Render a hybrid Factur-X / ZUGFeRD PDF/A-3 from EN 16931 JSON (2 credits).\n\nThe Invoice model validates and RECOMPUTES every total, so a malformed or\nself-contradictory invoice is a 422 before any credit hold is taken. The\nmoney path (hold -> build -> self-check -> settle/refund) lives in\napp/pipeline.py, shared with validate so the two cannot drift apart.\n\nReturns the PDF itself rather than JSON: it is the artifact the customer\npaid for, and base64-wrapping it would double the transfer for nothing.","body":{"mode":"raw","raw":"{}","options":{"raw":{"language":"json"}}}}},{"name":"Render Try","request":{"method":"GET","header":[],"url":{"raw":"{{base_url}}/v1/render/try","host":["{{base_url}}"],"path":["v1","render","try"]},"description":"No-key trial for GENERATION — the render counterpart of /v1/validate/try.\n\nRenders a FIXED curated EN16931 sample (never a caller's invoice) so a prospect sees\na real, self-checked Factur-X PDF without a key or signup. The sample is byte-stable\nand cached (sample_pdf's lru_cache), so per-hit cost is ~serving bytes; the per-IP\nburst limit — the same guard validate/try uses — is the abuse bound. We deliberately\ndo NOT consume the validate trial monthly cap: clicking the render demo must not spend\na prospect's validate allowance (different funnel, different meter)."}},{"name":"Signup","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{base_url}}/v1/signup","host":["{{base_url}}"],"path":["v1","signup"]},"description":"Self-serve free API key — email in, key out (shown ONCE).\n\nDeliberately UNAUTHENTICATED because it is how a stranger gets their first\nkey, so its only guard is an IP rate limit; the blast radius of abuse is\nbounded anyway by the free tier (50 validate runs/month per key). The account\nstarts on the 'free' plan; buying credits promotes it to 'paid' (see the\nStripe webhook). An existing e-mail returns its account and a fresh key —\ncreate_account keeps the existing plan, so this never downgrades a payer.","body":{"mode":"raw","raw":"{}","options":{"raw":{"language":"json"}}}}},{"name":"Validate","request":{"method":"POST","header":[{"key":"X-API-Key","value":"{{api_key}}"},{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{base_url}}/v1/validate","host":["{{base_url}}"],"path":["v1","validate"]},"description":"","body":{"mode":"raw","raw":"{}","options":{"raw":{"language":"json"}}}}},{"name":"Validate Try","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{base_url}}/v1/validate/try","host":["{{base_url}}"],"path":["v1","validate","try"]},"description":"No-key trial (blueprint §6) — the top of the funnel.\n\nSame engines, same honest engine statuses, no signup. Deliberately outside\nthe credit path: there is no account, so nothing to reserve, settle, or\nrecord in usage_events (whose account_id is NOT NULL). The IP cap replaces\nthe credit hold as the bound on cost, and is enforced in the same place the\nhold would be — before any engine runs.","body":{"mode":"raw","raw":"{}","options":{"raw":{"language":"json"}}}}},{"name":"Vat Attestation Key","request":{"method":"GET","header":[],"url":{"raw":"{{base_url}}/v1/vat/attestation-key","host":["{{base_url}}"],"path":["v1","vat","attestation-key"]},"description":"The Ed25519 PUBLIC key that signs VAT-verify attestations, so anyone can\nverify an evidence certificate OFFLINE (no account, no call to us). Raw\n32-byte key, base64. 503 while signing is unconfigured."}},{"name":"Vat Batch","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{base_url}}/v1/vat/batch","host":["{{base_url}}"],"path":["v1","vat","batch"]},"description":"Verify a LIST of EU VAT ids in one call (VAT-Watch). Billed per WELL-FORMED id\n(N ids -> N credits); a malformed id is reported but not charged, a VIES outage\nrefunds the whole batch (503), and only real valid/invalid verdicts are billed —\nthe same null rule as /v1/vat/verify, applied to a batch. Verdict-only: for a\nsingle SIGNED certificate (+ optional consultation number) use /v1/vat/verify.\nMoney path (reserve N -> lookup -> settle/refund) is app/pipeline.run_vat_batch.","body":{"mode":"raw","raw":"{}","options":{"raw":{"language":"json"}}}}},{"name":"Vat Verify","request":{"method":"POST","header":[{"key":"X-API-Key","value":"{{api_key}}"}],"url":{"raw":"{{base_url}}/v1/vat/verify","host":["{{base_url}}"],"path":["v1","vat","verify"]},"description":"Verify an EU VAT id against the official VIES service and return the verdict\nPLUS a signed Ed25519 evidence certificate (1 credit). Paid counterpart of the\nfree /v1/vat/verify/try: the credit buys the ATTESTATION — an offline-verifiable\nproof (against the key at /v1/vat/attestation-key) that FacturProof asserted this\nvalid/invalid verdict at this time — not the bare lookup. Money path (hold ->\nVIES -> sign -> settle/refund) lives in app/pipeline.run_vat_verify; the null\nrule applies (malformed id -> refunded 422, VIES outage -> refunded 503).\n\nPass requester_vat (your own EU VAT id) to obtain VIES's OFFICIAL consultation\nnumber inside the signed certificate — the strongest evidence, cross-checkable\nwith the tax authority. A malformed or VIES-rejected requester is a refunded 422\n(your input), never a charge."}},{"name":"Vat Verify Try","request":{"method":"GET","header":[],"url":{"raw":"{{base_url}}/v1/vat/verify/try","host":["{{base_url}}"],"path":["v1","vat","verify","try"]},"description":"No-key trial for VAT VERIFICATION — checks an EU VAT id against the official\nEU VIES service and returns valid/invalid plus the registered name/address.\n\nRate-limited per IP like render/try (its OWN scope, does NOT consume the\nvalidate/extract trial pool): a lookup is a single external EU call, so a\nper-IP burst limit is the right abuse bound. Honest on failure — if VIES is\ndown or gives no clean verdict we return 503 'vat_service_unavailable', never\na fabricated 'valid' (the null rule, applied to an external dependency). The\nreceive-side companion to the France mandate: a business must trust a\nsupplier's VAT id before it can reclaim the tax."}},{"name":"Vat Watches List","request":{"method":"GET","header":[],"url":{"raw":"{{base_url}}/v1/vat/watches","host":["{{base_url}}"],"path":["v1","vat","watches"]},"description":"List the supplier VATs this account monitors (VAT-Watch). Free — moves no\ncredits. `limit` is the subscription entitlement (0 = no active subscription),\n`used` the number of active watches."}},{"name":"Vat Watches Create","request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{base_url}}/v1/vat/watches","host":["{{base_url}}"],"path":["v1","vat","watches"]},"description":"Register (or re-arm) a supplier VAT for nightly monitoring (VAT-Watch).\n\nRequires an active subscription (accounts.vat_watch_limit > 0). A NEW watch is\nrefused with 402 once the active count reaches the plan's limit, but RE-ARMING an\nalready-active id (updating its webhook) is always allowed and consumes no slot.\nMoves NO credits — the flat monthly subscription is the meter, not per-call\ncredits. Set webhook_url + webhook_secret to receive signed change-alerts.","body":{"mode":"raw","raw":"{}","options":{"raw":{"language":"json"}}}}},{"name":"Vat Watches Delete","request":{"method":"DELETE","header":[],"url":{"raw":"{{base_url}}/v1/vat/watches","host":["{{base_url}}"],"path":["v1","vat","watches"]},"description":"Stop monitoring a supplier VAT. Idempotent — deleting an unknown id returns 200\nwith deleted=false, never a 404. Frees a slot against the subscription limit."}}]}