Developer guide · API v1 · Contract 1.12

Build on certified truth.

Everything you need to submit evidence to MGE, read back sealed certified records, and verify them. Every example on this page is real output from MGE, produced from synthetic data.

REST · JSONOpenAPI 3.1Scoped API keysPublicly verifiable

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.

DoorWho uses itEndpoints
Write (Contract №1)Whoever submits evidencePOST /certify · POST /certify/batch · GET /jobs/{id} · GET /modules · GET /modules/{id}/schema
Read (Contract №2)Whoever displays resultsGET /vault · GET /vault/{id} · GET /vault/{id}/evidence · GET /vault/{id}/exportpack · GET /recoveries · GET /rules
Public (no key)AnyoneGET /vault/{id}/verify · GET /keys · GET /openapi.json
Base URL. Production: 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.
MGE certifies; it does not recover money or give advice. It holds no disputes, letters or tasks. Your product presents MGE's certified facts under your own brand.

Five minutes

Quickstart

  1. 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.
  2. Submit evidence for one location, one module, one period:
    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.json
    The body (abridged; the complete synthetic example is in Submit evidence):
    {
      "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 …"
      ]
    }
    MGE accepts the work and answers at once with 202 Accepted:
    {
      "job_id": "job_576765b2e8185a33c4082ace",
      "status": "queued",
      "record_count": 9,
      "estimated_ms": 200,
      "accepted_at": "2026-09-24T15:02:17.000Z"
    }
  3. Poll the job until status is certified or failed:
    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) …"
    }
  4. Read the sealed record with your read key:
    curl https://api.fohboh.ai/mge/v1/vault/vr_a2e1f8d6429ae4afb0f912833c8c6827 \
      -H "Authorization: Bearer $MGE_READ_KEY"
    The outcome is in certified_metrics.outcome; a certified job is not the same as a CERTIFIED outcome (see Outcomes).
  5. 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:

ScopeMeaning
IntegrationYour integration id, for example acme-pos.
Location groupA set of your client's locations. A key acts only on locations inside its group.
Doorwrite or read. A write key cannot read vault records; a read key cannot submit evidence. GET /jobs/{id} and GET /modules accept either.
Environmentsandbox (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-Expires header, 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.
SituationStatuserror.code
No Authorization header, or an unknown or revoked key401UNAUTHENTICATED
An expired key (rotate before the date in MGE-Key-Expires)401KEY_EXPIRED
The key's door cannot use this endpoint (for example a read key on /certify)401KEY_SCOPE
The location is outside the key's location group401LOCATION_NOT_IN_SCOPE
A record that belongs to another account404NOT_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 pathKeyPurpose
POST /certifywriteSubmit one certification (202, asynchronous).
POST /certify/batchwriteSubmit 1–50 certifications in one call.
GET /jobs/{job_id}write or readJob status; the full vault record once certified.
GET /moduleswrite or readThe live module registry: status and maximum records per request.
GET /modules/{module_id}/schemawrite or readThe JSON Schema of records[] for that module.
GET /vaultreadYour sealed records, newest first, with filters.
GET /vault/{vault_id}readOne sealed record.
GET /vault/{vault_id}/evidencereadThe evidence index, or a Merkle inclusion proof with ?record_id=.
GET /vault/{vault_id}/exportpackreadThe ExportPack: received data, evidence with inclusion proofs, the record, narrative, keys and a SHA-256 manifest.
GET /recoveriesreadThe recovery ledger: which credits paid back earlier findings.
GET /rulesreadRule counts by category. Rule identifiers, logic and weights are never exposed.
GET /actions · POST /actions/{action_id}/resolve · GET /patternsreadFollow-ups MGE raises after certification, and weekly patterns across records, for integrations that act on them.
GET /vault/{vault_id}/verifynonePublic seal verification.
GET /keysnoneMGE's public signing keys, including retired ones.
GET /openapi.jsonnoneOpenAPI 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.

FieldTypeRequiredNotes
module_idM01 · M02 · M04yesOnly active modules certify (see Modules).
location_idstringyesMust be inside your key's location group.
source_systemstringyesA canonical key for the module, for example doordash.
period{ start, end }, ISO datesyesExactly one fiscal period of your client's calendar (a calendar month by default). M01 uses one processor statement period of 1–35 days.
recordsarray, 1–10,000yesThe module's evidence records; each has a record_id unique in the request and a record_type.
idempotency_keystring, 1–128noOr the Idempotency-Key header. See Idempotency.
test_runbooleannoA TEST RUN is certified twice and never supersedes. Always true on sandbox keys.
directionUNILATERAL · INBOUND · OUTBOUNDnoDefault UNILATERAL. INBOUND and OUTBOUND need a bidirectional plan.
paired_vault_idvr_…OUTBOUND onlyThe 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

statusMeaning
queuedAccepted and stored; accepted work survives a server restart.
processingBeing certified.
certifiedSealed. vault_id and the full record are present.
failedNot 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 codeMeaningWhat to do
PERIOD_NOT_FISCALThe period is not one whole fiscal period (M01: not a 1–35 day statement period).Send the fiscal or statement period.
SCHEMA_VIOLATIONA record failed an intake check the request schema cannot express.Fix the record and resubmit.
NO_STATEMENT · NO_INVOICEThe module's anchor document is missing.Include the statement or invoice.
ORPHAN_LINEA line with no statement or invoice header.Include the header.
DUPLICATE_ORDER · DUPLICATE_STATEMENTAn id appears twice.De-duplicate.
MIXED_PROCESSORM01: records from more than one processor.Certify each processor account separately.
MODULE_NOT_ACTIVEThe module stopped accepting certifications.Contact FohBoh.
DETERMINISM_FAILURE · INTERNAL_ERRORAn 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 with 400 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_id is 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

ModuleWhat it certifiessource_system keysStatus
M01 Merchant Processing FeesCard processor charges against the processing agreementtoast heartland square stripe worldpay fiserv chase other_processorActive
M02 Delivery Platform FeesDelivery platform charges against the merchant agreementdoordash ubereats grubhubActive
M04 Supplier InvoicesDistributor invoices against contract prices and receivingsysco usfoods pfg gfs other_distributorActive
M03 Royalty Fee RecoveryFranchisor royalty and fund chargesfranchisor_statement pos_exportNot 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

FieldMeaning
vault_idRecord id (vr_ + 32 hex), derived from the seal hash.
cert_refHuman reference, for example CRT-2026-0001.
tenant_id · location_id · module_id · periodAs certified.
fiscal_period · period_label · cadenceThe client's fiscal period and cadence.
stateS1–S7 (see Outcomes).
certified_atAn input to the seal.
sha256_hashThe seal hash: use it for public verification.
content_sha256The content fingerprint: identical for any two certifications of the same evidence under the same settings and versions.
merkle_rootRoot over your submitted evidence records (RFC 6962).
formula_version · rule_set_version · sector_packThe pinned versions the record was certified under.
certified_metricstrust_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.
narrativeThe sealed plain-language certification narrative and its SHA-256 (DCLS, the Deterministic Certified Language System).
manifest_sha256 · stage_chainHashes of the certification manifest and of each Trust Loop stage.
sealCustody metadata: standard, custodian, sealed time, jurisdiction, legal hold.
signatureMGE's signature over the sealed record (ED25519 or ECDSA_P256_SHA256); match key_id in GET /keys.
evidence_bundleReceipt for the write-once evidence bundle; null for TEST RUNs.
superseded_by · test_runSet 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

OutcomeTrust ScoreStateMeaning
CERTIFIED85–100S1Fully certified.
VERIFIED70–84S2Certified with discrepancies.
REVIEWED60–69, or any score with a hard-gate overrideS4Certified on bounded scope: the available evidence only.
FAILED0–59S6The evidence cannot support certification; gaps are itemized.
——S5Pending: no sealed record yet.
——S7Superseded 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"
}
Statuserror.codeWhen
400MALFORMED_REQUESTThe body is not valid JSON.
400SCHEMA_VIOLATIONThe request or a record fails the contract schema; violations gives the field path and your record_id.
401UNAUTHENTICATED · KEY_EXPIRED · KEY_SCOPE · LOCATION_NOT_IN_SCOPESee Authentication.
403PRODUCT_NOT_SUBSCRIBEDYour integration's product is not in the account's package.
404NOT_FOUNDNo such job, record, module or evidence record in your account.
409IDEMPOTENCY_CONFLICTThe key was already used for a different request.
422MODULE_NOT_ACTIVE · DIRECTION_NOT_CONTRACTED · PAIRING_INVALIDNot certifiable as asked.
429RATE_LIMITEDToo many requests; wait for the Retry-After seconds.
500SEAL_BROKEN · INTERNAL_ERRORAn MGE-side problem. Contact FohBoh with the request id.

Fair use

Rate limits

LimitPer minute
All requests, per key1,200
POST /certify and /certify/batch, per key300
Requests without a key (public verification), per address120

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 the Idempotency-Key header, 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: 202 with the same job_id and 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 429 after Retry-After, and 5xx with backoff and the same key. Never retry a 4xx unchanged.

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.json always 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

CapabilityStatus
WebhooksNot built. webhook_url is accepted but not called. Poll GET /jobs/{id}.
M03 Royalty Fee RecoveryContracted, not active: 422 MODULE_NOT_ACTIVE.
Legal seal levelseal_level: "legal" is accepted; no legal threshold is defined yet, so it produces a standard record.
Job status partialReserved 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_id from any error.