Demo environment. DemoCorp Manufacturing is fictional, every invoice is synthetic, and the ERP is a mock.
BridgeOpsSource produces. Policy decides. Humans authorize. Verification confirms.

API docs

How to test BridgeOps from a terminal: send invoices in through every intake (REST, signed webhook, CSV, AI extractor, manual form), watch policy decide, approve as a finance manager and verify the audit chain.

All data here is mock-up data. DemoCorp, its suppliers, purchase orders, users, keys and the ERP are synthetic. The demo resets every 6 hours.

Quick start

Base URL https://bridgeops.enthernetservice.com/api. Send a token or key as Authorization: Bearer ….

# 1. An ERP-bound invoice over 5,000 USD arrives over REST
curl -s -X POST -H "Authorization: Bearer demo-key-rest" -H "Content-Type: application/json" \
  -d '{"workflow_type":"invoice","payload":{"supplier_id":"SUP-1001","invoice_id":"INV-Q1","amount":"8900.00","currency":"USD","purchase_order":"PO-5001"}}' \
  https://bridgeops.enthernetservice.com/api/v1/events
# → {"workflow_id":"wf_…","status":"APPROVAL_REQUIRED","ai_used":"NO",…}

# 2. Look at it
curl -s -H "Authorization: Bearer demo-token-u_view" https://bridgeops.enthernetservice.com/api/v1/workflows/<workflow_id>

# 3. The operator can't approve it…
curl -s -X POST -H "Authorization: Bearer demo-token-u_ops" -H "Content-Type: application/json" \
  -d '{"reason":"looks fine"}' https://bridgeops.enthernetservice.com/api/v1/workflows/<workflow_id>/approve
# → 403 role OPERATOR lacks permission 'approve'

# 4. …the finance manager can, and BridgeOps writes it to the ERP and reads it back
curl -s -X POST -H "Authorization: Bearer demo-token-u_finance" -H "Content-Type: application/json" \
  -d '{"reason":"Matches PO-5001"}' https://bridgeops.enthernetservice.com/api/v1/workflows/<workflow_id>/approve

# 5. Check the audit chain
curl -s -H "Authorization: Bearer demo-token-u_audit" https://bridgeops.enthernetservice.com/api/v1/audit/verify

Try it

Requests go to this server's API with the credential above.

Response appears here.

Credentials

Demo credentials for synthetic data only; the app refuses to start with them outside demo mode. User tokens can also be fetched with POST /v1/demo/login. Each machine key only works for its own intake.

Bearer token or keyWho / what
demo-token-u_ownerowner@democorp.example · OWNER · everything
demo-token-u_opsops@democorp.example · OPERATOR · submit, run scenarios, correct, requeue
demo-token-u_financefinance.manager@democorp.example · APPROVER · approve, reject
demo-token-u_auditauditor@democorp.example · AUDITOR · read, verify the audit chain
demo-token-u_viewviewer@democorp.example · VIEWER · read only
demo-key-restREST intake key (source: rest) · POST /v1/events
demo-key-csvCSV intake key (source: csv) · POST /v1/csv
demo-key-aiAI extractor key (source: ai) · POST /v1/ai/events
whsec_demo_syntheticWebhook signing secret for source supplier-portal

Endpoints

Intake (machines)

POST/v1/eventsSubmit an invoice from a system over REST

Who: REST API key (demo-key-rest)

curl -X POST -H "Authorization: Bearer demo-key-rest" -H "Content-Type: application/json" \
  -H "Idempotency-Key: my-invoice-1" \
  -d '{"workflow_type":"invoice","payload":{"supplier_id":"SUP-1001","invoice_id":"INV-T1","amount":"1200.00","currency":"USD","purchase_order":"PO-5001"}}' \
  https://bridgeops.enthernetservice.com/api/v1/events
{"workflow_id": "wf_69ef998d5c0742c98aab", "status": "POLICY_CHECKED", "ai_used": "NO", "failure_reason": null}

Body must be exactly {workflow_type, payload}; the tenant comes from the key. Sending the same Idempotency-Key again returns the same workflow instead of a second invoice. Money is a decimal string.

POST/v1/webhooks/supplier-portalSigned webhook from the supplier portal

Who: HMAC signature (secret whsec_demo_synthetic)

BODY='{"workflow_type":"invoice","payload":{"supplier_id":"SUP-1001","invoice_id":"INV-T1","amount":"1200.00","currency":"USD","purchase_order":"PO-5001"}}'
TS=$(date +%s)
SIG="v1=$(printf '%s' "$TS.$BODY" | openssl dgst -sha256 -hmac whsec_demo_synthetic | sed 's/^.* //')"
curl -X POST -H "Content-Type: application/json" \
  -H "X-BridgeOps-Timestamp: $TS" -H "X-BridgeOps-Signature: $SIG" -H "X-BridgeOps-Delivery: dlv-$TS" \
  -d "$BODY" https://bridgeops.enthernetservice.com/api/v1/webhooks/supplier-portal
{"workflow_id": "wf_8b048c8646ab46698ef6", "status": "POLICY_CHECKED", "ai_used": "NO", "failure_reason": null}

Signature: v1=hex(HMAC-SHA256(secret, "<timestamp>." + raw_body)). A stale timestamp or a reused signature is rejected with 401 ("replayed request"); X-BridgeOps-Delivery is the idempotency key.

POST/v1/csvUpload a CSV of invoices; one workflow per row

Who: CSV API key (demo-key-csv) or a user who can submit

printf 'supplier_id,invoice_number,total,currency,purchase_order\nSUP-1001,INV-C1,300.00,USD,PO-5001\n' | \
  curl -X POST -H "Authorization: Bearer demo-key-csv" -H "Content-Type: text/csv" --data-binary @- \
  https://bridgeops.enthernetservice.com/api/v1/csv
{"workflows": [{"workflow_id": "wf_29cf…", "status": "POLICY_CHECKED", "ai_used": "NO", "failure_reason": null}]}

Required columns: supplier_id, invoice_number, total, currency, purchase_order. UTF-8, up to 2 MB / 5,000 rows. A bad row fails on its own (for example SCHEMA_FAILED: "amount: must be a decimal number").

POST/v1/ai/eventsRaw output from an AI extractor, treated as untrusted input

Who: AI API key (demo-key-ai)

curl -X POST -H "Authorization: Bearer demo-key-ai" -H "Content-Type: application/json" \
  -d '{"extracted":{"supplier_id":"SUP-1001","invoice_id":"INV-55120","amount":"4250.00","currency":"USD","purchase_order":"PO-5001"},"confidence":0.96,"source_document_id":"DOC-7781"}' \
  https://bridgeops.enthernetservice.com/api/v1/ai/events
{"workflow_id": "wf_0cc3…", "status": "SCHEMA_FAILED", "ai_used": "YES", "failure_reason": "AI output is not valid JSON"}  ← if you send chatty text instead

Every extracted value must appear in the referenced source document (DOC-7781 or DOC-7782), confidence below 0.90 needs an APPROVER, and output with extra keys (for example an injected "action": "POST_DIRECTLY_TO_ERP") is rejected as SCHEMA_FAILED. The workflow is stamped AI USED: YES.

People

POST/v1/manualSubmit an invoice by hand

Who: OWNER, OPERATOR

curl -X POST -H "Authorization: Bearer demo-token-u_ops" -H "Content-Type: application/json" \
  -d '{"workflow_type":"invoice","payload":{"supplier_id":"SUP-1001","invoice_id":"INV-T1","amount":"1200.00","currency":"USD","purchase_order":"PO-5001"}}' https://bridgeops.enthernetservice.com/api/v1/manual
POST/v1/workflows/{id}/approveApprove a held invoice

Who: APPROVER, OWNER (never the person who submitted or corrected it)

curl -X POST -H "Authorization: Bearer demo-token-u_finance" -H "Content-Type: application/json" \
  -d '{"reason":"Matches PO-5001"}' https://bridgeops.enthernetservice.com/api/v1/workflows/<id>/approve
{"workflow_id": "wf_7710…", "status": "APPROVED", "ai_used": "NO", "failure_reason": null}

The approval is bound to the payload hash: correcting the payload voids it. An operator gets 403 {"error":"PermissionDenied","detail":"role OPERATOR lacks permission 'approve'"}.

POST/v1/workflows/{id}/rejectReject a held invoice

Who: APPROVER, OWNER

curl -X POST -H "Authorization: Bearer demo-token-u_finance" -H "Content-Type: application/json" \
  -d '{"reason":"Duplicate of INV-55120"}' https://bridgeops.enthernetservice.com/api/v1/workflows/<id>/reject
POST/v1/workflows/{id}/correctFix the payload of a failed workflow and run it again

Who: OPERATOR, OWNER

curl -X POST -H "Authorization: Bearer demo-token-u_ops" -H "Content-Type: application/json" \
  -d '{"payload":{"supplier_id":"SUP-1001","invoice_id":"INV-C2","amount":"300.00","currency":"USD","purchase_order":"PO-5001"}}' \
  https://bridgeops.enthernetservice.com/api/v1/workflows/<id>/correct
POST/v1/workflows/{id}/requeue · /reverify · /cancelRecovery actions for dead letters, failed verification or cancelling

Who: OPERATOR, OWNER

cancel takes {"reason": "…"}; requeue and reverify take no body.

Reading

GET/v1/meWho the token belongs to

Who: any user token

curl -H "Authorization: Bearer demo-token-u_owner" https://bridgeops.enthernetservice.com/api/v1/me
{"id": "u_owner", "email": "owner@democorp.example", "role": "OWNER", "tenant_id": "democorp", "demo_mode": true, "ai_enabled": true}
GET/v1/dashboardCounters, connector health and circuit-breaker state

Who: any user token

curl -H "Authorization: Bearer demo-token-u_view" https://bridgeops.enthernetservice.com/api/v1/dashboard
{"active": 0, "completed": 1, "approval_required": 1, "failed": 1, "retrying": 0, "dead_letter": 0, "needs_human": 2, "total": 4, "ai_assisted": 1,
 "connectors": [{"id": "legacy-erp", "type": "http_erp", "circuit": "CLOSED", "healthy": true}, …]}
GET/v1/workflows?status=…&limit=100List workflows, newest first

Who: any user token

curl -H "Authorization: Bearer demo-token-u_view" "https://bridgeops.enthernetservice.com/api/v1/workflows?status=APPROVAL_REQUIRED,SCHEMA_FAILED"

status is a comma-separated list of states; limit max 500.

GET/v1/workflows/{id}Everything about one workflow

Who: any user token

curl -H "Authorization: Bearer demo-token-u_view" https://bridgeops.enthernetservice.com/api/v1/workflows/<id>
{"workflow": {"id": "wf_…", "status": "COMPLETED", "business_key": "invoice:SUP-1001|INV-62058", "normalized_payload": {…},
   "policy_decision": {"decision": "ALLOW", "facts": {"po_amount": "12000.00", "po_match": true, "supplier_active": true}, "reasons": [...]}},
 "normalized_event": {…}, "ai_used": "NO", "approvals": [], "executions": [{"attempt": 1, "status": "SUCCEEDED", "response_reference": "INV-9202"}],
 "audit": [{"event_type": "EVENT_RECEIVED", "event_hash": "ae78…", "previous_hash": "2072…"}, …]}
GET/v1/audit/verifyRecompute the tenant's audit hash chain

Who: AUDITOR, OWNER

curl -H "Authorization: Bearer demo-token-u_audit" https://bridgeops.enthernetservice.com/api/v1/audit/verify
{"ok": true, "checked": 41, "head": "485af775cb2a…"}
GET/healthService health

Who: anyone

curl https://bridgeops.enthernetservice.com/api/health

Demo controls

GET/v1/demo/usersThe demo users

Who: anyone

curl https://bridgeops.enthernetservice.com/api/v1/demo/users
POST/v1/demo/loginGet a demo user's token

Who: anyone

curl -X POST -H "Content-Type: application/json" -d '{"user_id":"u_ops"}' https://bridgeops.enthernetservice.com/api/v1/demo/login
{"token": "demo-token-u_ops", "synthetic": true}
POST/v1/demo/scenarios/{name}Run a scenario with a fresh synthetic invoice

Who: OPERATOR, OWNER

curl -X POST -H "Authorization: Bearer demo-token-u_ops" https://bridgeops.enthernetservice.com/api/v1/demo/scenarios/B

A = normal invoice, B = above the 5,000 USD threshold, C = AI extraction (C:malformed by default; also C:normal, C:uncertain, C:hallucinated, C:injected), D = ERP outage with retries (switches the mock ERP to down; put it back with erp-mode ok).

POST/v1/demo/erp-modeMake the mock ERP misbehave

Who: OPERATOR, OWNER

curl -X POST -H "Authorization: Bearer demo-token-u_owner" -H "Content-Type: application/json" \
  -d '{"mode":"ok"}' https://bridgeops.enthernetservice.com/api/v1/demo/erp-mode

Modes: ok, down, timeout_after_commit (writes then times out: tests reconcile-before-retry), lie (says yes, writes nothing: tests verification), corrupt, reject.

States & policy

RECEIVED → AUTHENTICATED → NORMALIZED → VALIDATED → POLICY_CHECKED → (APPROVAL_REQUIRED → APPROVED) → EXECUTING → VERIFYING → COMPLETED

Stops that need a human: SCHEMA_FAILED, POLICY_BLOCKED, APPROVAL_REQUIRED, CONNECTOR_FAILED, VERIFICATION_FAILED, DEAD_LETTERED. Retries: RETRY_PENDING (demo delays 10s / 30s / 60s). Final: COMPLETED, CANCELLED, APPROVAL_REJECTED, AUTH_FAILED.

Invoice policy (invoice-default v1)

RuleThenWhen
supplier-activeBLOCKSupplier is unknown or inactive (SUP-1002 is inactive)
po-matchBLOCKInvoice doesn't match an open purchase order on supplier, currency or amount (PO-5001: USD 12,000 · PO-5002: USD 3,000)
currency-threshold-definedBLOCKCurrency has no approval threshold (only USD is configured)
high-valueREQUIRE_APPROVALAmount above 5,000 USD needs an APPROVER
ai-low-confidenceREQUIRE_APPROVALAI-extracted and confidence below 0.90 needs an APPROVER
(default)ALLOWNo rule matched: create the payable in the legacy ERP, then verify it

Missing fields, type mismatches or a missing policy all mean BLOCK. The most restrictive matching rule wins, and the ERP destination comes from the policy, never from the payload.

Errors

Errors look like {"error": "PermissionDenied", "detail": "role OPERATOR lacks permission 'approve'"}.

400Invalid JSON
401Missing, unknown or wrong kind of credential; bad, stale or replayed webhook signature
403Role lacks the permission, or separation of duties (you can't approve your own submission)
404Not found, or another tenant's workflow
413Body too large (256 KB for events, 2 MB for CSV)
422Wrong body shape, missing X-BridgeOps-Delivery, unknown scenario or ERP mode
429Rate limited by the server