Causa Prima Invoices API
The Invoices API from Causa Prima — 3 operation(s) for invoices.
The Invoices API from Causa Prima — 3 operation(s) for invoices.
openapi: 3.1.0
info:
title: Scribo Invoice Invoices API
version: 1.0.0
description: Compliant e-invoice generation. **Phase 1 supports Germany (XRechnung / ZUGFeRD) and the United States (plain PDF) only** — any other sender jurisdiction is rejected with `unsupported_jurisdiction`. The `/api/v1/*` namespace served from `scribo.causaprima.ai` is the public surface — anonymous and rate-limited. The internal `/internal/scribo/*` namespace is used by the Causa Prima web app and MCP server and is not part of this contract.
contact:
name: Causa Prima
url: https://causaprima.ai
servers:
- url: https://scribo.causaprima.ai
description: Production
- url: http://localhost:3200
description: Local development (scribo Next.js proxy)
tags:
- name: Invoices
paths:
/api/v1/invoices:
post:
summary: Generate a compliant e-invoice
description: Anonymous endpoint. Rate-limited per source IP. Returns 201 with a download URL.
parameters:
- schema:
type: string
minLength: 1
maxLength: 255
description: Client-supplied key. Replaying the same key with identical inputs returns the cached invoice; replaying with different inputs returns 422 idempotency_key_mismatch.
required: false
name: Idempotency-Key
in: header
- schema:
type: string
minLength: 1
description: Email-ownership token from POST /api/v1/scribo/email-verifications/{id}/redeem. Required for the first invoice per sender email — without it (or a scribo session cookie) an unverified sender receives 401 email_verification_required.
required: false
name: X-Email-Verification-Token
in: header
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/CreateInvoiceInput'
responses:
'201':
description: Invoice generated and persisted
content:
application/json:
schema:
$ref: '#/components/schemas/Invoice'
'400':
description: Invalid input or downstream validator rejection
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorEnvelope'
'401':
description: Sender email not verified yet (email_verification_required) — run the email-verification flow and retry with X-Email-Verification-Token
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorEnvelope'
'403':
description: Verified email does not match sender.contact_email (verification_email_mismatch), or tenant is temporarily soft-blocked after repeated hard-limit hits (tenant_soft_blocked)
content:
application/json:
schema:
anyOf:
- $ref: '#/components/schemas/ErrorEnvelope'
- $ref: '#/components/schemas/TenantSoftBlockError'
'422':
description: Idempotency-Key mismatch
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorEnvelope'
'429':
description: Rate limit exceeded
headers:
Retry-After:
schema:
type: integer
description: Seconds until the limit resets.
content:
application/json:
schema:
$ref: '#/components/schemas/RateLimitError'
tags:
- Invoices
/api/v1/invoices/{id}:
get:
summary: Fetch invoice metadata
description: Returns the Invoice for a previously-generated invoice. Read path is wired alongside the scribo session cookie; cross-tenant probes return 404.
parameters:
- schema:
type: string
format: uuid
description: Invoice UUID
required: true
name: id
in: path
responses:
'200':
description: Invoice metadata
content:
application/json:
schema:
$ref: '#/components/schemas/Invoice'
'404':
description: Invoice not found or not visible to this caller
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorEnvelope'
tags:
- Invoices
/api/v1/invoices/{id}/download:
get:
summary: Download the invoice file
description: Streams the generated PDF (or hybrid PDF/A-3 with embedded XML) bytes. Future revisions may 302 to a signed object-storage URL.
parameters:
- schema:
type: string
format: uuid
required: true
name: id
in: path
responses:
'200':
description: Invoice bytes
content:
application/pdf:
schema:
type: string
format: binary
'404':
description: Artifact not found
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorEnvelope'
tags:
- Invoices
components:
schemas:
Invoice:
type: object
properties:
invoice_id:
type: string
format: uuid
document_id:
type: string
format: uuid
format:
type: string
enum:
- zugferd_comfort
- zugferd_basic
- xrechnung_cii
- xrechnung_ubl
- plain_pdf
download_url:
type: string
format: uri
download_url_expires_at:
type: string
format: date-time
preview_url:
type: string
format: uri
preview_url_expires_at:
type: string
format: date-time
submission:
type: object
properties:
status:
type: string
enum:
- not_provided
message:
type: string
manual_upload_hints:
type: array
items:
type: object
properties:
label:
type: string
url:
type: string
format: uri
leitweg_prefix:
type: string
required:
- label
- url
default: []
planned:
type: array
items:
type: string
default: []
required:
- status
- message
validator_summary:
type: object
properties:
valid:
type: boolean
validator:
type: string
errors:
type: array
items:
type: object
properties:
path:
type: string
rule:
type: string
message:
type: string
required:
- message
default: []
required:
- valid
- validator
magic_link_sent:
type: boolean
recipient_email_sent:
type: boolean
required:
- invoice_id
- document_id
- format
- download_url
- download_url_expires_at
- validator_summary
- magic_link_sent
RateLimitError:
type: object
properties:
error:
type: object
properties:
code:
type: string
enum:
- rate_limited
- tenant_invoice_quota_exceeded
example: tenant_invoice_quota_exceeded
message:
type: string
retry_after_seconds:
type: integer
minimum: 0
reset_at:
type: string
format: date-time
limit_code:
type: string
required:
- code
- message
- retry_after_seconds
- reset_at
- limit_code
required:
- error
CreateInvoiceInput:
type: object
properties:
sender:
type: object
properties:
legal_name:
type: string
minLength: 1
maxLength: 200
country_code:
type: string
pattern: ^[A-Z]{2}$
address_line1:
type: string
minLength: 1
maxLength: 200
address_line2:
type: string
maxLength: 200
postcode:
type: string
minLength: 1
maxLength: 20
city:
type: string
minLength: 1
maxLength: 100
tax_id:
type: string
minLength: 1
maxLength: 40
contact_email:
type: string
format: email
contact_phone:
type: string
minLength: 3
maxLength: 40
contact_name:
type: string
minLength: 1
maxLength: 200
tax_registration_id:
type: string
minLength: 1
maxLength: 50
required:
- legal_name
- country_code
- address_line1
- postcode
- city
- contact_email
recipient:
type: object
properties:
legal_name:
type: string
minLength: 1
maxLength: 200
country_code:
type: string
pattern: ^[A-Z]{2}$
address_line1:
type: string
minLength: 1
maxLength: 200
address_line2:
type: string
maxLength: 200
postcode:
type: string
minLength: 1
maxLength: 20
city:
type: string
minLength: 1
maxLength: 100
tax_id:
type: string
minLength: 1
maxLength: 40
contact_email:
type: string
format: email
leitweg_id:
type: string
minLength: 1
maxLength: 50
required:
- legal_name
- country_code
- address_line1
- postcode
- city
- contact_email
line_items:
type: array
items:
type: object
properties:
description:
type: string
minLength: 1
maxLength: 500
quantity:
type: string
pattern: ^(?:[1-9][0-9]*|0\.[0-9]*[1-9][0-9]*|[1-9][0-9]*\.[0-9]+)$
unit_code:
type: string
minLength: 1
maxLength: 10
default: EA
unit_price:
type: string
pattern: ^(?:0|[1-9][0-9]*)(?:\.[0-9]+)?$
tax_rate:
type: string
pattern: ^(?:0|[1-9][0-9]*)(?:\.[0-9]+)?$
tax_category_code:
type: string
enum:
- S
- Z
- E
- AE
- K
- G
- O
discount:
oneOf:
- type: object
properties:
type:
type: string
enum:
- amount
value:
type: string
pattern: ^(?:0|[1-9][0-9]*)(?:\.[0-9]+)?$
reason:
type: string
minLength: 1
maxLength: 200
required:
- type
- value
- reason
- type: object
properties:
type:
type: string
enum:
- percent
value:
type: string
pattern: ^(?:0|[1-9][0-9]*)(?:\.[0-9]+)?$
reason:
type: string
minLength: 1
maxLength: 200
required:
- type
- value
- reason
tax_exemption_code:
type: string
pattern: ^VATEX-(EU|[A-Z]{2})-[A-Z0-9-]+$
tax_exemption_reason:
type: string
minLength: 1
maxLength: 1000
required:
- description
- quantity
- unit_price
- tax_rate
- tax_category_code
minItems: 1
maxItems: 500
currency:
type: string
pattern: ^[A-Z]{3}$
jurisdiction:
type: string
pattern: ^[A-Z]{2}$
format_override:
type: string
enum:
- zugferd_comfort
- zugferd_basic
- xrechnung_cii
- xrechnung_ubl
- plain_pdf
notes:
type: string
maxLength: 1000
invoice_number:
type: string
pattern: ^[\p{L}\p{N} \-/.#_]{1,64}$
description: 'Optional invoice number printed on the document (BT-1). Omit the field entirely to have the server assign one — do NOT send a placeholder like "auto", "TBD", or "N/A" (these are ignored and a real code is assigned). When present: 1-64 chars of letters, digits, space, or - / . # _.'
issue_date:
type: string
pattern: ^\d{4}-\d{2}-\d{2}$
description: Optional ISO date (BT-2) — the invoice's issue date. Defaults to today when omitted.
due_date:
type: string
pattern: ^\d{4}-\d{2}-\d{2}$
payment_terms:
type: string
minLength: 1
maxLength: 200
payment_means:
type: object
properties:
type:
type: string
enum:
- credit_transfer
iban:
type: string
pattern: ^[A-Z]{2}\d{2}[A-Z0-9]{11,30}$
bic:
type: string
pattern: ^[A-Z][A-Z0-9]{7}([A-Z0-9]{3})?$
account_number:
type: string
pattern: ^\d{4,17}$
routing_number:
type: string
pattern: ^\d{9}$
account_name:
type: string
minLength: 1
maxLength: 200
bank:
type: string
minLength: 1
maxLength: 300
required:
- type
description: 'BG-16 payment instructions for a credit transfer. Provide EXACTLY ONE of: a SEPA `iban`, OR US domestic details (`account_number` + 9-digit ABA `routing_number`). An optional `bic` (SWIFT) may accompany either form — US accounts carry a SWIFT/BIC for inbound international wires. An optional free-text `bank` (beneficiary bank name + address) may also be included; it''s shown on the invoice but not validated structurally. Supplying both account forms, neither, or only half of the US pair is rejected. XRechnung (German B2G) requires the `iban` form (BR-DE-1).'
delivery_date:
type: string
pattern: ^\d{4}-\d{2}-\d{2}$
delivery_period:
type: object
properties:
start:
type: string
pattern: ^\d{4}-\d{2}-\d{2}$
end:
type: string
pattern: ^\d{4}-\d{2}-\d{2}$
required:
- start
- end
required:
- sender
- recipient
- line_items
- currency
ErrorEnvelope:
type: object
properties:
error:
type: object
properties:
code:
type: string
example: invalid_input
message:
type: string
details: {}
required:
- code
- message
required:
- error
TenantSoftBlockError:
type: object
properties:
error:
type: object
properties:
code:
type: string
enum:
- tenant_soft_blocked
message:
type: string
retry_after_seconds:
type: integer
minimum: 0
reset_at:
type: string
format: date-time
limit_code:
type: string
enum:
- tenant_hard_limit_hits_per_hour
details:
type: object
properties:
reason_code:
type: string
enum:
- tenant_hard_limit_hits_per_hour
auto_lift_at:
type: string
format: date-time
required:
- reason_code
- auto_lift_at
required:
- code
- message
- retry_after_seconds
- reset_at
- limit_code
- details
required:
- error
securitySchemes:
bearerAuth:
type: http
scheme: bearer
description: Optional API key for elevated quotas. Unauthenticated requests are accepted and rate-limited per IP.