Start here
Overview
MGE, the Metrics Governance Engine, certifies evidence. You send one location's evidence for one module and one fiscal period; MGE runs a deterministic certification, with no AI anywhere in it, and seals the result as an append-only vault record carrying a Trust Score (0–100), an outcome, certified findings, reconciliations and a cryptographic seal that anyone can verify.
The API has two doors and a public surface. Each key opens exactly one door.
| Door | Who uses it | Endpoints |
|---|---|---|
| Write (Contract №1) | Whoever submits evidence | POST /certify · POST /certify/batch · GET /jobs/{id} · GET /modules · GET /modules/{id}/schema |
| Read (Contract №2) | Whoever displays results | GET /vault · GET /vault/{id} · GET /vault/{id}/evidence · GET /vault/{id}/exportpack · GET /recoveries · GET /rules |
| Public (no key) | Anyone | GET /vault/{id}/verify · GET /keys · GET /openapi.json |
https://api.fohboh.ai/mge/v1. Your sandbox host and keys arrive with your access sheet during onboarding.
The machine-readable reference, OpenAPI 3.1 generated from the same contract package, is always at GET /mge/v1/openapi.json;
a dated copy is available to download.Five minutes
Quickstart
- Get sandbox keys. Request sandbox access; during onboarding FohBoh issues a write key and a read key (
mge_write_sbx_…,mge_read_sbx_…). Every sandbox certification is a TEST RUN: never billed, deletable, and never written to write-once storage. - Submit evidence for one location, one module, one period:The body (abridged; the complete synthetic example is in Submit evidence):
curl -X POST https://api.fohboh.ai/mge/v1/certify \ -H "Authorization: Bearer $MGE_WRITE_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: loc-0142:M02:doordash:2026-08-01:r1" \ --data @m02-august.jsonMGE accepts the work and answers at once with{ "module_id": "M02", "location_id": "loc-0142", "source_system": "doordash", "period": { "start": "2026-08-01", "end": "2026-08-31" }, "idempotency_key": "loc-0142:M02:doordash:2026-08-01:r1", "records": [ { "record_id": "AGR-1", "record_type": "dsp_agreement", "dsp": "doordash", "agreement_id": "DD-MA-2026", "agreement_sha256": "436bfe7866cfe9535a210904c9c6ac09a51780350fa7561e1ae28bb6c0f45144", "signed_date": "2025-12-15", "effective_start": "2026-01-01", "plan_tier": "Plus", "commission_bps": 2000, "commission_basis": "subtotal_ex_tax" }, { "record_id": "FT-ERR", "record_type": "dsp_fee_term", "dsp": "doordash", "agreement_id": "DD-MA-2026", "charge_class": "error_charge", "authorized": true, "per": "order", "max_per_order": 1 }, { "record_id": "STMT-08", "record_type": "dsp_statement", "dsp": "doordash", "statement_id": "DD-2026-08", "period_start": "2026-08-01", "period_end": "2026-08-31", "issued_date": "2026-09-03", "source_format": "doordash.merchant_statement.v1", "order_count": 812, "gross_sales_cents": 3150000, "net_payout_cents": 2517850, "source_document_sha256": "1fa6d2048f8ab60e2d4a3154d46c66337ba7ee4decaac6654c908ac4228c22ff" }, "… 6 more records: statement lines, an order charge, POS sales, a bank deposit, fulfillment proof …" ] }202 Accepted:{ "job_id": "job_576765b2e8185a33c4082ace", "status": "queued", "record_count": 9, "estimated_ms": 200, "accepted_at": "2026-09-24T15:02:17.000Z" } - Poll the job until
statusiscertifiedorfailed:curl https://api.fohboh.ai/mge/v1/jobs/job_576765b2e8185a33c4082ace \ -H "Authorization: Bearer $MGE_WRITE_KEY"{ "job_id": "job_576765b2e8185a33c4082ace", "status": "certified", "module_id": "M02", "location_id": "loc-0142", "vault_id": "vr_a2e1f8d6429ae4afb0f912833c8c6827", "error": null, "created_at": "2026-10-10T01:10:33.330Z", "record": "… the full vault record (below) …" } - Read the sealed record with your read key:The outcome is in
curl https://api.fohboh.ai/mge/v1/vault/vr_a2e1f8d6429ae4afb0f912833c8c6827 \ -H "Authorization: Bearer $MGE_READ_KEY"certified_metrics.outcome; a certified job is not the same as a CERTIFIED outcome (see Outcomes). - Verify the seal, no key needed, so your client or a counterparty can check it too:
curl "https://api.fohboh.ai/mge/v1/vault/vr_a2e1f8d6429ae4afb0f912833c8c6827/verify?sha256_hash=a2e1f8d6429ae4afb0f912833c8c6827d378e5dc711dd126067191e2700f8b54"{ "vault_id": "vr_a2e1f8d6429ae4afb0f912833c8c6827", "valid": true, "superseded_by": null, "signature_valid": true }
Security
Authentication & keys
Every call except the public ones sends a bearer key:
Authorization: Bearer mge_write_sbx_<your key>Each key is scoped to integration × location group × door, and to one environment:
| Scope | Meaning |
|---|---|
| Integration | Your integration id, for example acme-pos. |
| Location group | A set of your client's locations. A key acts only on locations inside its group. |
| Door | write or read. A write key cannot read vault records; a read key cannot submit evidence. GET /jobs/{id} and GET /modules accept either. |
| Environment | sandbox (mge_…_sbx_…): every certification is a TEST RUN. production (mge_write_…, mge_read_…): real, billable certifications, issued only after FohBoh's production approval. |
- Shown once. FohBoh shows a key one time when it is issued; MGE keeps only its SHA-256 fingerprint. Store it in your secret manager immediately. FohBoh never sends keys by email.
- Expiry. Sandbox keys last 30 days and production keys up to a year. Every authenticated response carries an
MGE-Key-Expiresheader, so you can rotate in good time. - Rotation. Ask FohBoh for a new key, switch over, then have the old key revoked. Both work during the overlap.
- No OAuth. MGE uses scoped API keys only; there is no user sign-in on the API.
| Situation | Status | error.code |
|---|---|---|
No Authorization header, or an unknown or revoked key | 401 | UNAUTHENTICATED |
| An expired key (rotate before the date in MGE-Key-Expires) | 401 | KEY_EXPIRED |
| The key's door cannot use this endpoint (for example a read key on /certify) | 401 | KEY_SCOPE |
| The location is outside the key's location group | 401 | LOCATION_NOT_IN_SCOPE |
| A record that belongs to another account | 404 | NOT_FOUND (MGE never confirms that it exists) |
A read key used on /certify, as returned by MGE:
{
"error": {
"code": "KEY_SCOPE",
"message": "A read key cannot use this endpoint"
},
"request_id": "req_433gh1x8sf"
}Reference
Endpoints
| Method and path | Key | Purpose |
|---|---|---|
POST /certify | write | Submit one certification (202, asynchronous). |
POST /certify/batch | write | Submit 1–50 certifications in one call. |
GET /jobs/{job_id} | write or read | Job status; the full vault record once certified. |
GET /modules | write or read | The live module registry: status and maximum records per request. |
GET /modules/{module_id}/schema | write or read | The JSON Schema of records[] for that module. |
GET /vault | read | Your sealed records, newest first, with filters. |
GET /vault/{vault_id} | read | One sealed record. |
GET /vault/{vault_id}/evidence | read | The evidence index, or a Merkle inclusion proof with ?record_id=. |
GET /vault/{vault_id}/exportpack | read | The ExportPack: received data, evidence with inclusion proofs, the record, narrative, keys and a SHA-256 manifest. |
GET /recoveries | read | The recovery ledger: which credits paid back earlier findings. |
GET /rules | read | Rule counts by category. Rule identifiers, logic and weights are never exposed. |
GET /actions · POST /actions/{action_id}/resolve · GET /patterns | read | Follow-ups MGE raises after certification, and weekly patterns across records, for integrations that act on them. |
GET /vault/{vault_id}/verify | none | Public seal verification. |
GET /keys | none | MGE's public signing keys, including retired ones. |
GET /openapi.json | none | OpenAPI 3.1 reference. |
All paths are relative to the base URL ending in /mge/v1.
Contract №1
Submit evidence: POST /certify
One request certifies one location × one module × one period, with up to 10,000 records.
| Field | Type | Required | Notes |
|---|---|---|---|
module_id | M01 · M02 · M04 | yes | Only active modules certify (see Modules). |
location_id | string | yes | Must be inside your key's location group. |
source_system | string | yes | A canonical key for the module, for example doordash. |
period | { start, end }, ISO dates | yes | Exactly one fiscal period of your client's calendar (a calendar month by default). M01 uses one processor statement period of 1–35 days. |
records | array, 1–10,000 | yes | The module's evidence records; each has a record_id unique in the request and a record_type. |
idempotency_key | string, 1–128 | no | Or the Idempotency-Key header. See Idempotency. |
test_run | boolean | no | A TEST RUN is certified twice and never supersedes. Always true on sandbox keys. |
direction | UNILATERAL · INBOUND · OUTBOUND | no | Default UNILATERAL. INBOUND and OUTBOUND need a bidirectional plan. |
paired_vault_id | vr_… | OUTBOUND only | The INBOUND certification this decision answers. |
The complete synthetic M02 (delivery fees) request used throughout this guide. It validates against the contract and certified as shown:
{
"module_id": "M02",
"location_id": "loc-0142",
"source_system": "doordash",
"period": {
"start": "2026-08-01",
"end": "2026-08-31"
},
"idempotency_key": "loc-0142:M02:doordash:2026-08-01:r1",
"records": [
{
"record_id": "AGR-1",
"record_type": "dsp_agreement",
"dsp": "doordash",
"agreement_id": "DD-MA-2026",
"agreement_sha256": "436bfe7866cfe9535a210904c9c6ac09a51780350fa7561e1ae28bb6c0f45144",
"signed_date": "2025-12-15",
"effective_start": "2026-01-01",
"plan_tier": "Plus",
"commission_bps": 2000,
"commission_basis": "subtotal_ex_tax"
},
{
"record_id": "FT-ERR",
"record_type": "dsp_fee_term",
"dsp": "doordash",
"agreement_id": "DD-MA-2026",
"charge_class": "error_charge",
"authorized": true,
"per": "order",
"max_per_order": 1
},
{
"record_id": "STMT-08",
"record_type": "dsp_statement",
"dsp": "doordash",
"statement_id": "DD-2026-08",
"period_start": "2026-08-01",
"period_end": "2026-08-31",
"issued_date": "2026-09-03",
"source_format": "doordash.merchant_statement.v1",
"order_count": 812,
"gross_sales_cents": 3150000,
"net_payout_cents": 2517850,
"source_document_sha256": "1fa6d2048f8ab60e2d4a3154d46c66337ba7ee4decaac6654c908ac4228c22ff"
},
{
"record_id": "STMT-08-L1",
"record_type": "dsp_statement_charge",
"dsp": "doordash",
"statement_id": "DD-2026-08",
"line_number": 1,
"charge_class": "commission",
"description": "Commission",
"amount_cents": 630000,
"basis_cents": 3150000,
"rate_bps": 2000
},
{
"record_id": "STMT-08-L2",
"record_type": "dsp_statement_charge",
"dsp": "doordash",
"statement_id": "DD-2026-08",
"line_number": 2,
"charge_class": "error_charge",
"description": "Error charges",
"amount_cents": 2150
},
{
"record_id": "OC-50117-1",
"record_type": "dsp_order_charge",
"dsp": "doordash",
"order_id": "ORD-50117",
"order_date": "2026-08-14",
"statement_id": "DD-2026-08",
"line_number": 1,
"charge_class": "error_charge",
"amount_cents": 2150
},
{
"record_id": "POS-08",
"record_type": "pos_delivery_sales",
"dsp": "doordash",
"period_start": "2026-08-01",
"period_end": "2026-08-31",
"order_count": 812,
"gross_sales_cents": 3150000,
"source_document_sha256": "61a4a548153e305115e658ba2ab26199c9e2e1a92cec17106674378bbced08cd"
},
{
"record_id": "DEP-0904",
"record_type": "bank_deposit",
"dsp": "doordash",
"deposit_date": "2026-09-04",
"amount_cents": 2517850,
"source_document_sha256": "1ec72718b303e96406a5a58da363de2df5d0c3d904cca1addddcc0779e6875e9"
},
{
"record_id": "PF-50117",
"record_type": "order_fulfillment",
"dsp": "doordash",
"order_id": "ORD-50117",
"proof_type": "kds_completion",
"captured_at": "2026-08-14T19:42:10-05:00",
"document_sha256": "373d279185f44d9cababbfe3678dbaabc433e38dee2131f8b711eb6c55925def"
}
]
}Batch. POST /certify/batch takes { "requests": [ … 1–50 … ] } and returns 202 { "jobs": [ … ] } in request order.
A batch is not atomic: MGE stops at the first request it refuses and returns that error; the requests before it were accepted.
Put an idempotency_key on every batch request so resending after a fix is safe.
Asynchronous
Jobs & polling
| status | Meaning |
|---|---|
queued | Accepted and stored; accepted work survives a server restart. |
processing | Being certified. |
certified | Sealed. vault_id and the full record are present. |
failed | Not sealed; error.code says why. No vault record exists. |
Poll with backoff: start near estimated_ms, then slow down. Webhooks are not available yet.
| Job failure code | Meaning | What to do |
|---|---|---|
PERIOD_NOT_FISCAL | The period is not one whole fiscal period (M01: not a 1–35 day statement period). | Send the fiscal or statement period. |
SCHEMA_VIOLATION | A record failed an intake check the request schema cannot express. | Fix the record and resubmit. |
NO_STATEMENT · NO_INVOICE | The module's anchor document is missing. | Include the statement or invoice. |
ORPHAN_LINE | A line with no statement or invoice header. | Include the header. |
DUPLICATE_ORDER · DUPLICATE_STATEMENT | An id appears twice. | De-duplicate. |
MIXED_PROCESSOR | M01: records from more than one processor. | Certify each processor account separately. |
MODULE_NOT_ACTIVE | The module stopped accepting certifications. | Contact FohBoh. |
DETERMINISM_FAILURE · INTERNAL_ERROR | An MGE-side integrity or processing error, not your data. | Retry with the same idempotency key, then contact FohBoh. |
Deterministic by design
Request conventions
- Money is integer cents; rates are integer basis points (
2000= 20.00%). A float in a money field is refused with400 SCHEMA_VIOLATION; MGE never rounds for you. - Quantities and weights are decimal strings, for example
"12.5", at most 4 decimal places. - Dates are
YYYY-MM-DD; timestamps are ISO 8601 with an offset. - Document hashes are lowercase 64-hex SHA-256 of the source document you hold.
record_idis your own id (1–128 characters), unique in the request; findings, proofs and gaps cite it back to you. Record order does not matter.
Coverage
Modules
| Module | What it certifies | source_system keys | Status |
|---|---|---|---|
| M01 Merchant Processing Fees | Card processor charges against the processing agreement | toast heartland square stripe worldpay fiserv chase other_processor | Active |
| M02 Delivery Platform Fees | Delivery platform charges against the merchant agreement | doordash ubereats grubhub | Active |
| M04 Supplier Invoices | Distributor invoices against contract prices and receiving | sysco usfoods pfg gfs other_distributor | Active |
| M03 Royalty Fee Recovery | Franchisor royalty and fund charges | franchisor_statement pos_export | Not yet active |
Fetch each module's exact record schema from GET /modules/{module_id}/schema; it is generated from the contract and is always current.
Field-by-field record guides for each module are shared during onboarding.
Contract №2
Response schema: the vault record
| Field | Meaning |
|---|---|
vault_id | Record id (vr_ + 32 hex), derived from the seal hash. |
cert_ref | Human reference, for example CRT-2026-0001. |
tenant_id · location_id · module_id · period | As certified. |
fiscal_period · period_label · cadence | The client's fiscal period and cadence. |
state | S1–S7 (see Outcomes). |
certified_at | An input to the seal. |
sha256_hash | The seal hash: use it for public verification. |
content_sha256 | The content fingerprint: identical for any two certifications of the same evidence under the same settings and versions. |
merkle_root | Root over your submitted evidence records (RFC 6962). |
formula_version · rule_set_version · sector_pack | The pinned versions the record was certified under. |
certified_metrics | trust_score, outcome, hard_gate_overrides, penalty_rows (where coverage was lost and how to fix it), disputed_total_cents, findings, kpis, reconciliation, and where present recoveries, evidence_gaps, held_items. |
narrative | The sealed plain-language certification narrative and its SHA-256 (DCLS, the Deterministic Certified Language System). |
manifest_sha256 · stage_chain | Hashes of the certification manifest and of each Trust Loop stage. |
seal | Custody metadata: standard, custodian, sealed time, jurisdiction, legal hold. |
signature | MGE's signature over the sealed record (ED25519 or ECDSA_P256_SHA256); match key_id in GET /keys. |
evidence_bundle | Receipt for the write-once evidence bundle; null for TEST RUNs. |
superseded_by · test_run | Set when a later certification replaced this one; whether it is a TEST RUN. |
Findings carry ref (F-001 for recoveries, V-001 for items needing review), kind, a plain-language
label and description, amount_cents, and the evidence record ids that support it. Build your interface from these fields;
MGE never exposes rule identifiers, rule logic or scoring weights.
The record MGE returned for the example (abridged where marked):
{
"vault_id": "vr_a2e1f8d6429ae4afb0f912833c8c6827",
"cert_ref": "CRT-2026-0001",
"tenant_id": "acme-pos-demo",
"location_id": "loc-0142",
"module_id": "M02",
"period": {
"start": "2026-08-01",
"end": "2026-08-31"
},
"fiscal_period": "2026-08",
"cadence": "fiscal_period",
"period_label": "2026-08",
"state": "S1",
"certified_at": "2026-09-24T15:02:18.000Z",
"sha256_hash": "a2e1f8d6429ae4afb0f912833c8c6827d378e5dc711dd126067191e2700f8b54",
"content_sha256": "3f395fd3136918230a023d827410dcbbd6571f057fa6b0131ead39a7b48ec16f",
"merkle_root": "dafc82c84e961b3b95498bfeeb5794437f21cbc594cd9258299202a063354fcc",
"formula_version": "m02_dfr_v3",
"rule_set_version": "DCLS-2.0-LEGAL@a51868bf6a2cbe14",
"sector_pack": {
"id": "INST-01",
"name": "Restaurant MGE (INST-01 Restaurant pack)",
"version": "1.2.0"
},
"certified_metrics": {
"trust_score": 100,
"outcome": "CERTIFIED",
"hard_gate_overrides": [],
"penalty_rows": [],
"disputed_total_cents": 2150,
"findings": [
{
"ref": "F-001",
"kind": "RECOVERY",
"label": "Chargeback or error charge not supported by the evidence",
"description": "Order ORD-50117 (2026-08-14) was charged an error charge of $21.50; the restaurant's kitchen display completion shows the order was fulfilled",
"amount_cents": 2150,
"evidence": [
"OC-50117-1",
"PF-50117",
"STMT-08"
]
}
],
"kpis": {
"bank_deposits_cents": 2517850,
"commission_charged_cents": 630000,
"commission_contracted_cents": 630000,
"credits_cents": 0,
"dsp_gross_sales_cents": 3150000,
"error_charges_cents": 2150,
"marketing_charged_cents": 0,
"net_payout_cents": 2517850,
"order_count": 812,
"pos_gross_sales_cents": 3150000,
"pos_order_count": 812,
"statement_count": 1,
"total_charges_cents": 632150
},
"reconciliation": [
{
"leg": "DD-2026-08: gross sales POS↔DSP",
"left_cents": 3150000,
"right_cents": 3150000,
"variance_cents": 0,
"status": "MATCHED"
},
{
"leg": "DD-2026-08: statement footing",
"left_cents": 2517850,
"right_cents": 2517850,
"variance_cents": 0,
"status": "MATCHED"
},
"… every reconciliation leg …"
]
},
"seal": {
"fre_standard": "FRE 902(13)/(14) — self-authenticating electronic records",
"custodian": "FohBoh MGE",
"sealed_at": "2026-09-24T15:02:18.000Z",
"jurisdiction": "US",
"legal_hold": false
},
"narrative": {
"text": "DCLS certification narrative. Module M02 · location loc-0142 · fiscal period 2026-08 (2026-08-01 to 2026-08-31). 9 evidence records are committed under Merkle root dafc82c84e961b3b95498bfeeb5794437f21cbc594cd9258299202a063354fcc.\n1 certifie …",
"sha256": "be7362e0f3dbd0ad8891358a14555abbdd5245a34da6403b2bf168a29e194f3a"
},
"manifest_sha256": "bba894b78481942bc7b2cfd9095140359d26c5475676e359f5a27c505d2e6f73",
"stage_chain": [
{
"stage": "RESOLVE",
"output_sha256": "3447a5f847cf90e9f70250aa64eac381b4f4ae33f373fed201769d59f133ceda",
"link_sha256": "3703948c3767c0fd40381cdab5ce45bfbc72a4141abcd109cba7b251d08052ec"
},
{
"stage": "GOVERN",
"output_sha256": "0765eedec4a303e53505f0be9dc3b3a83f55b28baa2b8d13749c84bc6043492d",
"link_sha256": "c3b50daab9399bbc2af54e7d2c76697b5d6c71b4c752addca28fde26a6bda268"
},
"… one entry per Trust Loop stage …"
],
"signature": {
"key_id": "mge-seal-docs-1",
"algorithm": "ED25519",
"value": "BWVZHlFQDxCNuyFjlndcuycH…"
},
"evidence_bundle": null,
"superseded_by": null,
"test_run": true
}Published bands
Outcomes & states
| Outcome | Trust Score | State | Meaning |
|---|---|---|---|
| CERTIFIED | 85–100 | S1 | Fully certified. |
| VERIFIED | 70–84 | S2 | Certified with discrepancies. |
| REVIEWED | 60–69, or any score with a hard-gate override | S4 | Certified on bounded scope: the available evidence only. |
| FAILED | 0–59 | S6 | The evidence cannot support certification; gaps are itemized. |
| — | — | S5 | Pending: no sealed record yet. |
| — | — | S7 | Superseded by a later certification. |
- The Trust Score measures evidence coverage, not the number of problems. Findings never lower it: a record can be CERTIFIED and carry large findings. Missing mandatory evidence overrides the outcome to REVIEWED; it never deducts points.
- Every outcome is sealed. A FAILED outcome is a sealed record; a failed job produced no record.
- Sealed records are never updated or deleted. To correct evidence, submit a new certification for the same location, module, period and direction; the earlier record moves to S7 and names its replacement in
superseded_by. TEST RUNs never supersede.
Trust, but verify
Verification
Public check, no key: GET /vault/{vault_id}/verify?sha256_hash=<64 hex> returns valid, superseded_by and
signature_valid, and reveals nothing else about the record.
{
"vault_id": "vr_a2e1f8d6429ae4afb0f912833c8c6827",
"valid": true,
"superseded_by": null,
"signature_valid": true
}Prove one evidence record was included without revealing the others: GET /vault/{vault_id}/evidence?record_id=… returns an RFC 6962 inclusion proof.
Leaf hash = SHA-256(0x00 ‖ leaf); node hash = SHA-256(0x01 ‖ left ‖ right); the result must equal merkle_root.
{
"vault_id": "vr_a2e1f8d6429ae4afb0f912833c8c6827",
"merkle_root": "dafc82c84e961b3b95498bfeeb5794437f21cbc594cd9258299202a063354fcc",
"proof": {
"record_id": "PF-50117",
"leaf": "{\"captured_at\":\"2026-08-14T19:42:10-05:00\",\"document_sha256\":\"373d279185f44d9cababbfe3678dbaabc433e38dee2131f8b711eb6c55925def\",\"dsp\":\"doordash\",\"order_id\":\"ORD-50117\",\"proof_type\":\"kds_completion\",\"record_id\":\"PF-50117\",\"record_type\":\"order_fulfillment\"}",
"index": 4,
"tree_size": 9,
"proof": [
"e82e7108c7a1baaa247f60f69f447c5c32ce736906fb13a14ba76b8a9d47dc49",
"6eb0a176ed0e594e680a10f151447abd208fea93d3d1e831628adbbd4228464f",
"…"
]
}
}Determinism. Certification is a pure function of the evidence, the client's pinned settings and the pinned formula and rule versions.
The same evidence under the same versions gives the same content_sha256; the seal hash differs only because certified_at is sealed with it.
Every TEST RUN is certified twice and must reproduce its own hash.
When things go wrong
Error handling
Every error, on every endpoint, has the same envelope. Quote request_id when you contact FohBoh.
{
"error": {
"code": "SCHEMA_VIOLATION",
"message": "Request failed schema validation",
"violations": [
{
"field": "records.0.amount_cents",
"record_id": "STMT-08-L1",
"reason": "Expected integer, received float"
}
]
},
"request_id": "req_zmpdu2wy8i"
}| Status | error.code | When |
|---|---|---|
| 400 | MALFORMED_REQUEST | The body is not valid JSON. |
| 400 | SCHEMA_VIOLATION | The request or a record fails the contract schema; violations gives the field path and your record_id. |
| 401 | UNAUTHENTICATED · KEY_EXPIRED · KEY_SCOPE · LOCATION_NOT_IN_SCOPE | See Authentication. |
| 403 | PRODUCT_NOT_SUBSCRIBED | Your integration's product is not in the account's package. |
| 404 | NOT_FOUND | No such job, record, module or evidence record in your account. |
| 409 | IDEMPOTENCY_CONFLICT | The key was already used for a different request. |
| 422 | MODULE_NOT_ACTIVE · DIRECTION_NOT_CONTRACTED · PAIRING_INVALID | Not certifiable as asked. |
| 429 | RATE_LIMITED | Too many requests; wait for the Retry-After seconds. |
| 500 | SEAL_BROKEN · INTERNAL_ERROR | An MGE-side problem. Contact FohBoh with the request id. |
Fair use
Rate limits
| Limit | Per minute |
|---|---|
| All requests, per key | 1,200 |
POST /certify and /certify/batch, per key | 300 |
| Requests without a key (public verification), per address | 120 |
Over a limit, MGE answers 429 RATE_LIMITED with a Retry-After header in seconds. An address-level firewall limit also applies in front of the API.
Batch where you can, poll with backoff, and contact FohBoh if your volume needs higher limits.
Safe retries
Idempotency & retries
- MGE identifies a submission by its
idempotency_key, then theIdempotency-Keyheader, then the canonical SHA-256 of the request itself, so an identical body resent without a key returns the same job. - Same key, same request:
202with the samejob_idand its current status. Nothing is re-run and nothing is billed. - Same key, different request:
409 IDEMPOTENCY_CONFLICT. - Recommended key:
<location_id>:<module_id>:<period.start>:<revision>. Bump the revision when you deliberately re-certify corrected evidence. - Retry
429afterRetry-After, and5xxwith backoff and the same key. Never retry a4xxunchanged.
Stability
Versioning & deprecation
- The major version is in the path (
/mge/v1). Within a major version, changes are additive only: new optional fields, new endpoints, new values. Ignore fields you do not recognize. - A breaking change ships as a new major version, announced in advance; the previous major version keeps working for 12 months after the notice, so both versions run side by side.
- The contract is published as versioned schemas generated from one source;
GET /openapi.jsonalways describes the version you are calling. Partners receive the contract changelog during onboarding. - Sealed records keep the formula and rule-set versions they were certified under, forever.
Plainly stated
Not yet available
| Capability | Status |
|---|---|
| Webhooks | Not built. webhook_url is accepted but not called. Poll GET /jobs/{id}. |
| M03 Royalty Fee Recovery | Contracted, not active: 422 MODULE_NOT_ACTIVE. |
| Legal seal level | seal_level: "legal" is accepted; no legal threshold is defined yet, so it produces a standard record. |
Job status partial | Reserved in the contract; not produced. |
We would rather tell you than have you find out.
People, not tickets
Support & terms
- Onboarding: start with a sandbox access request; partners follow a ten-step track from sandbox to production; production keys are issued only after FohBoh's approval.
- Commercials: a license fee plus a per-certification toll; sandbox TEST RUNs, rejected requests and idempotent retries are never billed. Request pricing.
- Terms: use of the API is subject to your partner agreement and the API terms of use.
- No advisory services: the API returns governance and certification outputs only, never financial, accounting, legal, tax, labor or operational advice.
- Contact: hello@fohboh.com. Quote the
request_idfrom any error.