Skip to main content
This guide covers two operator workflows that both end in a downloadable or resolved artifact: export jobs (queue a report, poll until it succeeds, download the file) and disputes (open a dispute against an exception, attach evidence, then close it won or lost). Both are tenant-scoped from the JWT.

Export jobs


Exports are asynchronous. You create a job scoped to a context, poll its status by ID, and download the file once it reaches SUCCEEDED. Statuses are QUEUED, RUNNING, SUCCEEDED, FAILED, EXPIRED, and CANCELED.

Create an export job

POST to the context’s export-jobs collection. Responds 202 Accepted with the job ID and a poll URL.
curl -X POST "https://api.matcher.example.com/v1/contexts/{contextId}/export-jobs" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "reportType": "MATCHED",
    "format": "CSV",
    "dateFrom": "2025-01-01",
    "dateTo": "2025-01-31",
    "sourceId": "550e8400-e29b-41d4-a716-446655440000"
  }'
{
  "jobId": "550e8400-e29b-41d4-a716-446655440000",
  "status": "QUEUED",
  "statusUrl": "/v1/export-jobs/550e8400-e29b-41d4-a716-446655440001"
}
  • reportType — one of MATCHED, UNMATCHED, VARIANCE, EXCEPTIONS (aliases MATCHES and UNMATCHED_TRANSACTIONS are normalized).
  • formatCSV, JSON, or XML (normalized to uppercase).
  • dateFrom / dateTo — optional YYYY-MM-DD; dateFrom defaults to 30 days before dateTo, and dateTo defaults to tomorrow (UTC).
  • sourceId — optional source filter.
SUMMARY and PDF are not supported for async export jobs and are rejected with 400. The date window is also capped at a maximum span (an over-range request is rejected rather than silently clamped).

Poll job status

Read the top-level job route (the statusUrl from creation).
curl -X GET "https://api.matcher.example.com/v1/export-jobs/{jobId}" \
  -H "Authorization: Bearer $TOKEN"
{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "reportType": "MATCHED",
  "format": "CSV",
  "status": "SUCCEEDED",
  "recordsWritten": 4250,
  "bytesWritten": 524288,
  "fileName": "matched_report_2025-01-31.csv",
  "createdAt": "2025-01-15T10:30:00Z",
  "startedAt": "2025-01-15T10:30:05Z",
  "finishedAt": "2025-01-15T10:35:00Z",
  "expiresAt": "2025-01-16T10:30:00Z",
  "downloadUrl": "https://storage.example.com/exports/matched_report.csv?token=abc"
}
error is present only when status is FAILED; downloadUrl appears only once the job has SUCCEEDED and the file is still available. You can list a context’s jobs with GET /v1/contexts/{contextId}/export-jobs, list all jobs with GET /v1/export-jobs, and cancel a queued/running job with POST /v1/export-jobs/{jobId}/cancel.

Download the file

Returns a presigned URL, the original file name, a SHA-256 checksum, and the URL’s remaining lifetime in seconds.
curl -X GET "https://api.matcher.example.com/v1/export-jobs/{jobId}/download" \
  -H "Authorization: Bearer $TOKEN"
{
  "downloadUrl": "https://storage.example.com/exports/report.csv?token=abc",
  "fileName": "matched_report.csv",
  "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
  "expiresIn": 3600
}
Export files are purged after expiresAt (default 7 days). Once a job is EXPIRED the file is no longer downloadable — re-run the export to regenerate it.

Disputes


A dispute is opened against a specific exception when a reconciliation discrepancy needs to be contested. Its lifecycle is DRAFTOPENPENDING_EVIDENCEWON / LOST.

Open a dispute

POST to the exception’s disputes collection.
curl -X POST "https://api.matcher.example.com/v1/exceptions/{exceptionId}/disputes" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "category": "BANK_FEE_ERROR",
    "description": "Transaction amount differs from invoice"
  }'
{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "exceptionId": "550e8400-e29b-41d4-a716-446655440001",
  "category": "BANK_FEE_ERROR",
  "state": "OPEN",
  "description": "Transaction amount differs from invoice",
  "openedBy": "user@example.com",
  "evidence": [],
  "createdAt": "2025-01-15T10:30:00Z",
  "updatedAt": "2025-01-15T10:30:00Z"
}
category is one of BANK_FEE_ERROR, UNRECOGNIZED_CHARGE, DUPLICATE_TRANSACTION, or OTHER. The opening principal is recorded in openedBy.

Submit evidence by URL

Attach a reference to an already-hosted evidence file plus a describing comment.
curl -X POST "https://api.matcher.example.com/v1/disputes/{disputeId}/evidence" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "comment": "Attached bank statement showing correct amount",
    "fileUrl": "https://storage.example.com/evidence/doc123.pdf"
  }'

Upload an evidence file

Stream the raw file bytes directly to tenant-scoped object storage — the comment travels as a query parameter and the file as the request body. Responds 201 Created with the updated dispute. Allowed content types are application/pdf, image/png, image/jpeg, and text/csv; the body is capped at 10 MiB.
curl -X POST "https://api.matcher.example.com/v1/disputes/{disputeId}/evidence/upload?comment=Bank%20statement%20showing%20correct%20amount" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/pdf" \
  --data-binary @statement.pdf
Each stored evidence item is returned on the dispute’s evidence array:
{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "disputeId": "550e8400-e29b-41d4-a716-446655440001",
  "comment": "Bank statement showing correct amount",
  "submittedBy": "user@example.com",
  "fileUrl": "https://storage.example.com/evidence/doc123.pdf",
  "submittedAt": "2025-01-15T10:30:00Z"
}
The upload endpoint fails closed with 503 when object storage is not configured, rejects oversize bodies with 413, and rejects content types outside the allowlist with 415. The tenant and dispute are always resolved from the JWT and path — never from the body.

Close a dispute

Record the outcome. won sets the terminal state to WON or LOST, with a required resolution note.
curl -X POST "https://api.matcher.example.com/v1/disputes/{disputeId}/close" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "won": true,
    "resolution": "Counterparty acknowledged the error and issued correction"
  }'
You can list disputes with GET /v1/disputes (filter by state, category, date range; sort and cursor-paginate) and fetch one with GET /v1/disputes/{disputeId}.

Response codes


StatusMeaning
200Export/dispute data returned
201Evidence file uploaded
202Export job accepted
400Invalid input (unsupported report type/format, bad date range, invalid category)
404Context, export job, exception, or dispute not found
409Invalid dispute state transition
413Evidence file exceeds the 10 MiB cap
415Evidence content type not in the allowlist
422Malformed field
503Export or evidence storage not configured