Law Intake API
Push legislative material to MainCompliance in whatever format you already produce. We store your bytes exactly as sent and process them later.
This API has one job: accept and preserve. There is no parsing, validation or transformation at upload time, so a delivery never fails because of its structure. XML, JSON, PDF, ZIP, HTML, plain text — all are accepted and stored verbatim.
Every accepted payload is written to durable storage, fingerprinted with SHA-256, and recorded with the time we received it, the supplier it came from, and the filename you declared. You get an identifier back that you can use to confirm receipt.
Base URL
All requests go to a single host over HTTPS. Plain HTTP is redirected and never carries a token.
https://dev2-api.maincompliance.com
curl -X POST https://dev2-api.maincompliance.com/v1/laws \ -H "Authorization: Bearer $MC_TOKEN" \ -H "Content-Type: application/xml" \ -H "X-Filename: 1999_1079.xml" \ --data-binary @1999_1079.xml
Authentication
Authenticate every request with the bearer token issued to you, in the Authorization header. Tokens identify which supplier a delivery came from, so keep yours private — anyone holding it can submit on your behalf.
Requests without a valid token receive 401 unauthorized. Nothing is stored and no detail about why the token failed is returned.
To rotate a token, ask us to issue a new one; both work during the overlap window so you can switch without downtime.
Authorization: Bearer mc_live_7f3c…
{ "error": { "code": "unauthorized", "message": "Missing or invalid bearer token." }, "request_id": "5c1f…" }
Errors
Errors use conventional HTTP status codes and always return the same JSON shape, so you can branch on error.code rather than parsing prose.
Every response — success or failure — carries an X-Request-Id header, repeated in the body. Quote it when reporting a problem and we can find the exact request in our logs.
Retrying
500, 507 and network failures are safe to retry — see Idempotency. Back off for a few minutes on 507: it means we are temporarily short of storage, and the condition is ours to clear, not yours. 401 and 413 will fail identically on retry; fix the token or the file size instead.
{ "error": { "code": "payload_too_large", "message": "Payload exceeds the 1073741824 byte limit." }, "request_id": "9a2e…" }
Upload a delivery
POST /v1/laws
Send the file as the raw request body. Do not wrap it in JSON or multipart form data — the body is the file, byte for byte, and that is exactly what we store.
Headers
Your bearer token: Bearer <token>.
Recorded as you declare it and returned unchanged. We never rely on it to decide how to store the bytes.
The original filename, e.g. 1999_1079.xml. Stored verbatim for traceability; a sanitised copy is used on disk. May also be given as the ?filename= query parameter.
Query parameters
Anything else you append is kept alongside the delivery as metadata — for example ?batch=2026-09-02&jurisdiction=SE. We do not interpret these; they travel with the file for whoever processes it.
Returns
201 with the delivery object when the bytes are new. 200 with duplicate: true when we already hold these exact bytes from you.
curl -X POST https://dev2-api.maincompliance.com/v1/laws \ -H "Authorization: Bearer $MC_TOKEN" \ -H "Content-Type: application/xml" \ -H "X-Filename: 1999_1079.xml" \ --data-binary @1999_1079.xml
// Node 18+ — streams the file, no buffering import { openAsBlob } from 'node:fs'; const body = await openAsBlob('1999_1079.xml'); const res = await fetch('https://dev2-api.maincompliance.com/v1/laws', { method: 'POST', headers: { Authorization: `Bearer ${process.env.MC_TOKEN}`, 'Content-Type': 'application/xml', 'X-Filename': '1999_1079.xml', }, body, }); console.log(await res.json());
import os, requests with open('1999_1079.xml', 'rb') as fh: r = requests.post( 'https://dev2-api.maincompliance.com/v1/laws', headers={ 'Authorization': f'Bearer {os.environ["MC_TOKEN"]}', 'Content-Type': 'application/xml', 'X-Filename': '1999_1079.xml', }, data=fh, # streamed, not loaded into memory timeout=900, ) print(r.json())
{ "id": "8f14e45f-ceea-4a1b-9f2c-1d3b4a5e6f70", "status": "received", "duplicate": false, "supplier": "interjust", "bytes": 84213, "sha256": "3b7f2c…", "received_at": "2026-09-02T07:14:22.481Z", "request_id": "5c1f…" }
Retrieve a delivery
GET /v1/laws/{id}
Confirm that a delivery arrived and see how far it has got. Use it to reconcile a batch after sending, or to check whether something you pushed has been processed yet.
You can only read your own deliveries. An id belonging to another supplier returns 404, the same as an id that does not exist.
Path parameters
The id returned when the delivery was accepted.
curl https://dev2-api.maincompliance.com/v1/laws/8f14e45f-ceea-4a1b-9f2c-1d3b4a5e6f70 \ -H "Authorization: Bearer $MC_TOKEN"
{ "id": "8f14e45f-ceea-4a1b-9f2c-1d3b4a5e6f70", "status": "received", "supplier": "interjust", "filename": "1999_1079.xml", "content_type": "application/xml", "bytes": 84213, "sha256": "3b7f2c…", "received_at": "2026-09-02T07:14:22.481Z" }
Health
GET /v1/health
Unauthenticated liveness check that also confirms the database is reachable. Use it to verify connectivity before starting a large batch.
Returns 200 with {"ok": true} when the service can serve uploads, and 503 when it cannot.
curl https://dev2-api.maincompliance.com/v1/health
{ "ok": true }
The delivery object
Returned whenever a delivery is accepted or retrieved.
Our identifier for this delivery. Keep it — it is how you ask about the file later.
received · processing · processed · failed. Everything starts as received; processing happens on our side afterwards.
true when we already held these exact bytes from you. See Idempotency.
Present on duplicates. true means we already had the record but no longer had the bytes, and your re-delivery put them back — a good reason to keep re-sending when in doubt.
Which supplier the token identified. You cannot set this yourself.
Exactly how many bytes we stored. Compare it with your file size to confirm nothing was truncated.
SHA-256 of the stored bytes, hex encoded. Compare it with your own hash for end-to-end integrity.
When we accepted it, in UTC (ISO 8601).
Identifier for this specific HTTP request. Quote it in support questions.
# your hash should equal the one we return shasum -a 256 1999_1079.xml
Idempotency
Re-sending a file is free and safe. We fingerprint every payload, so if you send bytes we already hold from you, we keep the original and return it with duplicate: true and status 200 instead of 201.
That means you never need to track what you have already sent. If a connection drops or you are unsure whether a delivery landed, simply send it again — you will get back the original id, and no second copy is stored.
Re-sending also repairs. If our record survived but the stored file did not, the duplicate response comes back with restored: true — your copy became the copy we keep. Sending again is never wasted.
{ "id": "8f14e45f-ceea-4a1b-9f2c-1d3b4a5e6f70", "status": "received", "duplicate": true, "received_at": "2026-09-02T07:14:22.481Z" }
Limits
Per request. Larger than any single statute we have seen; split genuinely larger batches into several requests. Exceeding it returns 413 payload_too_large.
Generous enough for slow links and large archives. Set your client timeout to match rather than something shorter.
We never inspect or reject on content. XML, JSON, PDF, ZIP, HTML, plain text — send what you have.
TLS is required. Send the body raw — not multipart form data, not base64 wrapped in JSON.
{ "error": { "code": "payload_too_large", "message": "Payload exceeds the 1073741824 byte limit." } }