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.

Base URL
https://dev2-api.maincompliance.com
Send a law file
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.

Keep tokens out of URLs. Query strings end up in proxy and browser logs. Always send the token in the header.

To rotate a token, ask us to issue a new one; both work during the overlap window so you can switch without downtime.

Authorization header
Authorization: Bearer mc_live_7f3c…
401 response
{
  "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.

201 created 200 duplicate 400 empty_payload 401 unauthorized 404 not_found 413 payload_too_large 507 insufficient_storage 500 internal_error

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 shape
{
  "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

AuthorizationstringRequired

Your bearer token: Bearer <token>.

Content-TypestringOptional

Recorded as you declare it and returned unchanged. We never rely on it to decide how to store the bytes.

X-FilenamestringOptional

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

Any keystringOptional

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.

Request
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
201 Created
{
  "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

iduuidRequired

The id returned when the delivery was accepted.

Request
curl https://dev2-api.maincompliance.com/v1/laws/8f14e45f-ceea-4a1b-9f2c-1d3b4a5e6f70 \
  -H "Authorization: Bearer $MC_TOKEN"
200 OK
{
  "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.

Request
curl https://dev2-api.maincompliance.com/v1/health
200 OK
{ "ok": true }

The delivery object

Returned whenever a delivery is accepted or retrieved.

iduuid

Our identifier for this delivery. Keep it — it is how you ask about the file later.

statusenum

received · processing · processed · failed. Everything starts as received; processing happens on our side afterwards.

duplicateboolean

true when we already held these exact bytes from you. See Idempotency.

restoredboolean

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.

supplierstring

Which supplier the token identified. You cannot set this yourself.

bytesinteger

Exactly how many bytes we stored. Compare it with your file size to confirm nothing was truncated.

sha256string

SHA-256 of the stored bytes, hex encoded. Compare it with your own hash for end-to-end integrity.

received_attimestamp

When we accepted it, in UTC (ISO 8601).

request_idstring

Identifier for this specific HTTP request. Quote it in support questions.

Verify integrity locally
# 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.

What counts as identical? The exact bytes, from the same supplier. Change one character and it is a new delivery, which is what you want when a statute is amended.

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.

200 OK — already held
{
  "id": "8f14e45f-ceea-4a1b-9f2c-1d3b4a5e6f70",
  "status": "received",
  "duplicate": true,
  "received_at": "2026-09-02T07:14:22.481Z"
}

Limits

Maximum payload1 GiB

Per request. Larger than any single statute we have seen; split genuinely larger batches into several requests. Exceeding it returns 413 payload_too_large.

Request timeout15 minutes

Generous enough for slow links and large archives. Set your client timeout to match rather than something shorter.

Formatsany

We never inspect or reject on content. XML, JSON, PDF, ZIP, HTML, plain text — send what you have.

TransportHTTPS only

TLS is required. Send the body raw — not multipart form data, not base64 wrapped in JSON.

413 Payload Too Large
{
  "error": {
    "code": "payload_too_large",
    "message": "Payload exceeds the 1073741824 byte limit."
  }
}