Encrypted Salary Envelope Workflow

From sealed payslip to verifiable envelope

An end-to-end guide for sending disclosed payroll metadata alongside sealed payslip bytes. Follow the sender, issuer, recipient, and verifier boundaries below, then try the live /payroll landing page or inspect the live envelope contract.

Seal locally, verify publicly

The payroll API is an envelope issuer and audit surface, not a payslip reader. The sender keeps the plaintext and decryption key in its own custody while the API receives the minimum metadata needed to identify, sign, hash, and persist the envelope.

01 · Sender

The employer or payroll integrator encrypts the payslip locally, retains the plaintext and key custody, and prepares only jurisdiction, gross, deductions, and opaque encrypted_payload for submission.

02 · Payroll API

POST /api/payroll/envelope receives those four fields. It never decrypts encrypted_payload; the sealed bytes remain opaque to this endpoint.

03 · Issuer

The server hashes the ciphertext, canonicalizes the envelope metadata, signs the canonical bytes with its Ed25519 issuer key, and persists the signed envelope for audit.

04 · Recipient / verifier

A recipient or downstream verifier checks the signed claim and disclosed metadata. If entitled to read the payslip, it follows an independently authorized decryption and key-delivery path outside this endpoint.

Access boundary. Issuing or verifying an envelope does not grant recipient access to salary plaintext. Recipient authorization, decryption, and key delivery remain outside the payroll API. See the live /payroll landing page for the product surface.

POST /api/payroll/envelope

JSON request with disclosed metadata and opaque encrypted_payload for the payslip bytes. One envelope per issuer signature; replay of the same inputs re-issues the same envelope deterministically.

Method
POST
URL
https://automa-labs-ai.polsia.app/api/payroll/envelope
Base
https://automa-labs-ai.polsia.app
Content-Type
application/json
Schema
payroll-env-1
Issuer
automaos.payroll
Discovery
GET https://automa-labs-ai.polsia.app/api/payroll/envelope

Jurisdiction-bound canonicalization

The issuer signs a canonical UTF-8 string built from a structured object whose keys are sorted alphabetically and rendered with no whitespace. The signed bytes cover envelope_id, jurisdiction, gross, deductions, ciphertext_hash, the issuer name, and issued_at. Two different jurisdictions therefore always produce two distinct signed envelopes — the signed canonical payload is the cross-border audit point, never the plaintext payslip.

Jurisdiction whitelist

The jurisdiction field must appear in the server-side whitelist. Unknown values return 400. The whitelist below is rendered straight from the live contract.

US UK SG DE JP BR

Canonicalization rule

rule
JSON.stringify(obj) with keys sorted alphabetically and no whitespace.

Privacy boundary

encrypted_payload stays sealed. It contains the encrypted payslip bytes and is never decrypted by the payroll API. The signed and disclosed metadata a verifier can see includes gross, deductions, jurisdiction, issuer, timestamp, envelope ID, and the ciphertext hash. Those fields support verification without exposing salary plaintext.

Issuer identity

The signing key is server-owned. When PAYROLL_ISSUER_PRIVATE_KEY is unset the endpoint still works but issues envelopes with an ephemeral key (mock_key: true); those envelopes will not verify across restarts. The fingerprint below is the 16-character SPKI body prefix a visitor can compare against the issuer… pill on /payroll to confirm both pages describe the same signing identity.

fingerprint issuer MCowBQYDK2VwAyEA…
mock_key — PAYROLL_ISSUER_PRIVATE_KEY is not set on the server. The full PEM below was generated at boot and is unstable across restart; envelope signatures will not verify once the process is replaced.
pem — issuer public key (SPKI Ed25519)
-----BEGIN PUBLIC KEY-----
MCowBQYDK2VwAyEAoMrDBSJicqCEyHma4ZgvEBRK7agzAEXz2yqFdoYAlss=
-----END PUBLIC KEY-----

Verify the seal without opening the payslip

The adjacent KAIOS receipt contract provides a reusable signed-receipt boundary. A receipt binds its canonical payload and disclosed fields; POST /api/kaios/verify checks the Ed25519 signature over the exact canonical payload_string and returns a verifier-safe verdict containing the signed issuer, timestamp, and disclosed fields.

Verification is not decryption. Neither KAIOS receipt verification nor this payroll documentation grants access to salary plaintext. A verifier can confirm the signed claim and its disclosed metadata while an independently authorized recipient retains the decryption key. Read the full KAIOS verification guide and its live receipt contract.

Required fields

All four fields are required. Any missing or invalid field produces a 400 with the offending fields listed in errors.

Field Type Description
jurisdiction string ISO-3166 alpha-2/3 jurisdiction code. Whitelisted server-side; unknown values return 400.
gross number Gross salary amount in the envelope's native currency. NUMERIC(18,2) on the server to avoid float drift on money.
deductions object 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 string Opaque encrypted payslip bytes. The server treats this as opaque and never decrypts; it is hashed and persisted verbatim for audit.

JSON body the integrator posts

The values below come straight from the live contract. This synthetic example contains disclosed gross and deductions metadata plus an opaque ciphertext placeholder; it is not a plaintext payslip or a real employee record.

json
{
  "jurisdiction": "US",
  "gross": 7500,
  "deductions": {
    "fica": 575,
    "federal": 1200
  },
  "encrypted_payload": "0x9f3a8c1d_demo_us_payslip"
}
The example's gross, deductions, and jurisdiction are intentionally disclosed API metadata. Only encrypted_payload represents the sealed payslip bytes, and the placeholder is synthetic.

curl invocation

One command — sends the example envelope to the live issuer. The host is whatever the request landed on; the path is the same everywhere.

bash — curl
curl -X POST https://automa-labs-ai.polsia.app/api/payroll/envelope \
  -H "Content-Type: application/json" \
  -d '{
  "jurisdiction": "US",
  "gross": 7500,
  "deductions": {
    "fica": 575,
    "federal": 1200
  },
  "encrypted_payload": "0x9f3a8c1d_demo_us_payslip"
}'
The endpoint is public — no Bearer header. Replace jurisdiction with any value from the whitelist pills above; gross and deductions follow the per-jurisdiction shape (US fica / federal, UK ni / paye, SG cpf, …); encrypted_payload is the opaque ciphertext bytes the browser produced — the server treats it as opaque and never decrypts it.

What the server can give back

Every terminal status the integrator needs to handle.

Status Body When
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)"} Envelope accepted: signed canonical payload persisted to payroll_envelopes; envelope_id and signature returned.
400 {"ok":"false","errors":["string"]} Validation error — one or more required fields are missing, wrong type, or jurisdiction is not in the whitelist.

The signed envelope, not the payslip bytes, is the audit point.

A US-founded operating company can pay a contractor in SG, a UK entity can pay a contractor in DE, and a JP employer can pay a contractor in BR — without shipping plaintext financial data across borders. Each envelope is jurisdiction-bound: a US envelope carries the fica / federal deduction shape, a UK envelope carries ni / paye, an SG envelope carries cpf, a DE envelope carries soli / kirchensteuer, a JP envelope carries shotoku, and a BR envelope carries inss. The JSONB deductions field fits each per-jurisdiction shape without schema churn, the envelope_id is part of the signed canonical payload so a regulator can confirm the issuance event without ever holding the payslip, and the encrypted_payload rides along inside the same JSON document as opaque ciphertext — hash it, persist it, audit it. Two distinct jurisdictions therefore always produce two distinct signed envelopes, and the issuance event is the audit point — never the plaintext payslip bytes.

What happens to a successful envelope

Every accepted envelope is written to the payroll_envelopes table, keyed UNIQUE on envelope_id, and indexed on jurisdiction. The same contract — including the example envelope and the response shapes — is published as plain JSON at https://automa-labs-ai.polsia.app/api/payroll/envelope, so an integrator can pull the spec straight off the live endpoint rather than caching this page. Return to the /payroll landing page to see the live issuer surface.