{"schema_version":"payroll-env-1","endpoint":"POST /api/payroll/envelope","discovery":"GET /api/payroll/envelope","auth":{"scheme":"Public endpoint; signing identity is server-owned (Ed25519).","issuer_env_var":"PAYROLL_ISSUER_PRIVATE_KEY","notes":"When PAYROLL_ISSUER_PRIVATE_KEY is unset the endpoint still works but issues envelopes signed with an ephemeral key (mock_key: true). Envelopes signed in mock mode do not verify across restarts."},"jurisdictions":{"whitelist":["US","UK","SG","DE","JP","BR"],"rule":"jurisdiction must appear in the whitelist; unknown jurisdictions are rejected with 400.","notes":"Per-jurisdiction deduction shapes vary (US: fica, UK: ni, SG: cpf, …) and fit the JSONB deductions field without schema churn."},"canonicalization":{"rule":"JSON.stringify(obj) with keys sorted alphabetically and no whitespace.","algorithm":"Builds the structured payload then renders it to a single UTF-8 string. That string is what the issuer signs. jurisdiction, gross, and the hash of the encrypted payload are all part of the signed bytes so two different jurisdictions produce two different signed envelopes."},"determinism":{"rule":"envelope_id = \"env_<sha256[:32]>\" over (jurisdiction + \":\" + gross.toFixed(2) + \":\" + JSON.stringify(deductions) + \":\" + ciphertext_hash + \":\" + issued_at_ms + \":\" + issuer).","replay":"Same input → same envelope_id, same signature (no DB write, replay: true in the response)."},"content_type":"application/json","required_fields":{"jurisdiction":{"type":"string","enum":["US","UK","SG","DE","JP","BR"],"description":"ISO-3166 alpha-2/3 jurisdiction code. Whitelisted server-side; unknown values return 400."},"gross":{"type":"number","min":0,"description":"Gross salary amount in the envelope's native currency. NUMERIC(18,2) on the server to avoid float drift on money."},"deductions":{"type":"object","description":"Per-jurisdiction deductions keyed by code (e.g. US: { fica, federal }, UK: { ni, paye }, SG: { cpf }). Each value is a number. JSONB so per-jurisdiction shapes fit one schema."},"encrypted_payload":{"type":"string","min_length":1,"max_bytes":65536,"description":"Opaque encrypted payslip bytes. The server treats this as opaque and never decrypts; it is hashed and persisted verbatim for audit."}},"responses":{"201":{"ok":"true","envelope_id":"string (env_<sha256[:32]>)","jurisdiction":"string","gross":"number","deductions":"object","encrypted_payload_hash":"string (64-hex sha256 over the opaque encrypted_payload)","signature":"string (128-hex Ed25519)","payload_string":"string (canonical UTF-8 bytes the issuer signed)","issuer":"string","issued_at":"ISO-8601 string","issuer_pub_key_pem":"string (PEM SPKI Ed25519)","mock_key":"boolean","replay":"boolean — true if this is a replay of a previously-issued envelope (no DB write)"},"400":{"ok":"false","errors":["string"]}},"example_envelope":{"jurisdiction":"US","gross":7500,"deductions":{"fica":575,"federal":1200},"encrypted_payload":"0x9f3a8c1d_demo_us_payslip"},"persistence":{"table":"payroll_envelopes","deterministic_key":"envelope_id (UNIQUE)","indexes":["payroll_envelopes_jurisdiction_idx"]}}