Report Endpoints
Endpoints for drafting regulatory reports from Orca data and reading them back.
Reports created through these endpoints are drafts. They are autopopulated from the data you send and placed in the approval queue in the Orca dashboard. Filing with the Financial Intelligence Centre happens only from the dashboard, on an explicit action by your reporting officer. See Introduction for the full flow.
Create Report
Draft a single report. Orca populates the goAML report structure from the subject, transactions and merchant you reference, plus your client configuration, and returns the draft’s identifier.
Endpoint
POST /v1/reportRequest Schema
| Field | Type | Required | Description |
|---|---|---|---|
reportType | enum | Yes | Report type: STR, SAR, CTR, IFTR, Other |
id | string | No | Your own identifier for this report. Used for idempotency, repeating a request with the same id returns the existing draft rather than creating a second one. Orca generates one if omitted |
reportNumber | string | No | Your internal report or case number, carried onto the filing |
title | string | No | Short report title. Autopopulated from the template or from the subject and trigger if omitted |
description | string | No | One-line summary of the report |
reason | string | No | Grounds for the report, the narrative that becomes the goAML transaction description and report reason. Autopopulated from the template if omitted |
action | string | No | Action taken by your institution (for example the resulting account status). Autopopulated from the template if omitted |
templateId | string | No | Identifier of a report template configured for your client. Populates title, reason and action, with any field you send taking precedence |
indicators | array | No | goAML reporting indicator codes, for example ["RIND124"]. Your reviewer can add or remove these in the dashboard |
caseOpenedAt | number | No | Unix epoch milliseconds, when the underlying case or alert was opened |
timestamp | number | No | Unix epoch milliseconds. Defaults to receipt time |
userId | string | Conditional | Identifier of the report subject. Omit if user is used. See Subject or transaction is required |
user | object | Conditional | Complete User object. Omit if userId is used. Use this if the subject was not previously sent to /v1/user. See Subject or transaction is required |
transactionIds | array | Conditional | Identifiers of transactions to include. Omit if transactions is used. See Subject or transaction is required |
transactions | array | Conditional | Array of complete Transaction objects. Omit if transactionIds is used. Use this if the transactions were not previously sent to /v1/transaction. See Subject or transaction is required |
merchantId | string | No | Identifier of the merchant party, where the report concerns a merchant. Omit if merchant is used |
merchant | object | No | Complete Merchant object. Omit if merchantId is used |
Referencing subjects and transactions
Every entity on a report can be supplied either by identifier, if you have already sent it to
Orca, or inline as a full object, if you have not. Reuse the same user, transaction and
merchant objects you already use elsewhere in the API; no reporting-specific shape is needed:
| You have | Send | Result |
|---|---|---|
Already called /v1/user | userId | Orca reads the stored user, including KYC, AML, salary and document detail |
Not called /v1/user | user | Orca creates or updates the user, then populates from it |
Already called /v1/transaction | transactionIds | Orca reads the stored transactions, including their triggered rules |
Not called /v1/transaction | transactions | Orca stores the transactions, then populates from them |
| A merchant party is involved | merchantId or merchant | Merchant becomes the counterparty on the filing |
Subject or transaction is required
A report needs something to be about. At least one of a subject or a transaction must be on the request — that is, at least one of
userId,user,transactionIdsortransactions. A request carrying none of them is rejected with aVALIDATION_ERRORand no draft is created.
Either group on its own is enough:
| Sent | Accepted | Notes |
|---|---|---|
userId or user | Yes | Subject-only report. Normal for a SAR, where there may be no concluded transaction |
transactionIds or transactions | Yes | Transaction-only report. Orca resolves the subject from the transactions where they carry one |
| Both | Yes | The usual call, and the most completely autopopulated |
| Neither | No | VALIDATION_ERROR, 400 |
merchantId and merchant do not satisfy this on their own — a merchant is a counterparty on the
filing, not the report subject.
{
"error": {
"type": "VALIDATION_ERROR",
"message": "Request validation failed",
"details": [
"/: at least one of `userId`, `user`, `transactionIds` or `transactions` is required"
]
},
"timestamp": 1754006400123
}Everything else on the request is optional. In practice userId plus transactionIds is the normal
call.
What Orca populates for you
You do not send goAML fields. Orca derives them:
| goAML area | Derived from |
|---|---|
| Report header: schema version, report date, report type | Request plus receipt time |
Reporting entity: rentityId, entity name, branch, registration number | Your client configuration |
| Reporting person and reporting location | Your client configuration and the submitting dashboard user |
| Transaction: number, amount, currency, transmission mode, date, description | The referenced transaction objects |
t_from / t_to parties: person, identity document, address, phone, occupation, employer | The referenced user and merchant objects |
| Account block: account number, type, institution, status, currency, balance | The referenced transaction and user objects |
| Indicators, reason, action | The request, or the report template |
Anything Orca cannot derive is left blank for your reviewer rather than guessed at.
Example Request
{
"reportType": "STR",
"reportNumber": "CASE-2026-00841",
"title": "STR: Deposits disproportionate to declared income",
"description": "High movement of deposits against declared monthly income",
"reason": "Subject completed deposits totalling ZAR 1 240 000 over 30 days against a declared monthly income of ZAR 28 000. Deposits originate from three unrelated banks and are not withdrawn.",
"action": "Account Status: Active. Subject to be reported for an STR due to high movement of deposits.",
"indicators": ["RIND002", "RIND049", "RIND124"],
"userId": "USER123",
"transactionIds": ["TXN123", "TXN124", "TXN125"],
"caseOpenedAt": 1753900000000,
"timestamp": 1754006400000
}Example Request: inline objects
Use this form when the subject and transactions have not been sent to Orca separately.
{
"reportType": "SAR",
"reason": "Attempted deposit declined on KYC mismatch; subject reattempted with altered identity detail.",
"indicators": ["RIND037"],
"user": {
"id": "USER456",
"firstName": "John",
"lastName": "Doe",
"dateOfBirth": "1990-01-01",
"phone": {
"phone": "+27821234567",
"country": "ZA",
"isVerified": true
},
"kycDetail": {
"kycStatus": "FAILED",
"sourceOfIncome": "self-employed"
},
"status": "BLOCKED"
},
"transactions": [
{
"id": "TXN789",
"userId": "USER456",
"status": "DECLINED",
"currencyCode": "ZAR",
"amount": 85000,
"direction": "payin",
"actionType": "deposit",
"paymentMethod": "bank",
"timestamp": 1754006400000
}
]
}Response Schema
| Field | Type | Description |
|---|---|---|
reportId | string | Orca identifier for the created draft |
status | string | Always draft for reports created over the API |
reportType | string | Report type as recorded |
reportNumber | string | Your internal report number, if supplied |
fiuReferenceNumber | string | FIC reference number. null until the report is filed from the dashboard |
createdAt | number | Unix epoch milliseconds |
requiresReview | boolean | Always true, the draft awaits human approval before it can be filed |
populated | object | Summary of what Orca autopopulated |
populated.user | boolean | Whether a subject was resolved and populated |
populated.merchant | boolean | Whether a merchant party was resolved and populated |
populated.transactions | number | Number of transactions attached to the report |
populated.indicators | number | Number of indicator codes recorded |
incomplete | array | Fields the reviewer must supply before the report can be filed |
Example Response
{
"reportId": "rep_01JQ4T8ZC7K2XN",
"status": "draft",
"reportType": "STR",
"reportNumber": "CASE-2026-00841",
"fiuReferenceNumber": null,
"createdAt": 1754006400123,
"requiresReview": true,
"populated": {
"user": true,
"merchant": false,
"transactions": 3,
"indicators": 3
},
"incomplete": [],
"timestamp": 1754006400123
}A draft with gaps returns them so you know what the reviewer will be asked for:
{
"reportId": "rep_01JQ4T9M18F0PB",
"status": "draft",
"reportType": "STR",
"fiuReferenceNumber": null,
"createdAt": 1754006400456,
"requiresReview": true,
"populated": {
"user": true,
"merchant": false,
"transactions": 1,
"indicators": 0
},
"incomplete": [
"indicators",
"user.documents",
"user.address"
],
"timestamp": 1754006400456
}Responses
200: Report draft created400: Bad request, including a request with nouserId,user,transactionIdsortransactions. See Error Reference401: Unauthorized403: Reporting not enabled for this client404: A referenceduserId,transactionIdsentry ormerchantIdwas not found500: Internal server error
Create Reports in Bulk
Draft many reports in one request. Each item is validated and populated independently, so a bad item does not discard the good ones. Use this for periodic compliance sweeps and backfills rather than for real-time reporting.
Endpoint
POST /v1/reports/bulkRequest Schema
| Field | Type | Required | Description |
|---|---|---|---|
reports | array | Yes | Array of report objects. Each entry uses exactly the Create Report request schema, and each must satisfy Subject or transaction is required on its own |
The default maximum batch size is 100 reports per request. Contact support@orca-fraud.com if you need a higher limit.
Example Request
{
"reports": [
{
"id": "CASE-2026-00841",
"reportType": "STR",
"reason": "Deposits disproportionate to declared income.",
"indicators": ["RIND002", "RIND124"],
"userId": "USER123",
"transactionIds": ["TXN123", "TXN124"]
},
{
"id": "CASE-2026-00842",
"reportType": "STR",
"reason": "Repeated transfers between linked accounts with no economic purpose.",
"indicators": ["RIND104"],
"userId": "USER456",
"transactionIds": ["TXN501"]
},
{
"id": "CASE-2026-00843",
"reportType": "CTR",
"userId": "USER789",
"transactionIds": ["TXN903"]
},
{
"id": "CASE-2026-00844",
"reportType": "Other",
"reason": "Merchant flagged during periodic review.",
"merchantId": "MERCH123456"
}
]
}Response Schema
| Field | Type | Description |
|---|---|---|
created | array | Reports drafted successfully |
created[].index | number | Zero-based position of the item in the request reports array |
created[].id | string | Your own identifier for the item, if supplied |
created[].reportId | string | Orca identifier for the created draft |
created[].status | string | Always draft |
created[].populated | object | Same shape as the single-report populated object |
created[].incomplete | array | Fields the reviewer must supply |
failed | array | Items that were rejected |
failed[].index | number | Zero-based position of the rejected item |
failed[].id | string | Your own identifier for the item, if supplied |
failed[].error | object | Error object in the standard error format |
summary.received | number | Items in the request |
summary.created | number | Drafts created |
summary.failed | number | Items rejected |
An item with no userId, user, transactionIds or transactions fails on its own with a
VALIDATION_ERROR and does not affect the rest of the batch.
Items in created are queued for review regardless of what is in failed. Retry only the failed
items, resending a successful item with the same id returns the existing draft rather than
creating a duplicate.
Example Response
{
"created": [
{
"index": 0,
"id": "CASE-2026-00841",
"reportId": "rep_01JQ4T8ZC7K2XN",
"status": "draft",
"populated": {
"user": true,
"merchant": false,
"transactions": 2,
"indicators": 2
},
"incomplete": []
},
{
"index": 1,
"id": "CASE-2026-00842",
"reportId": "rep_01JQ4T8ZC7K3PA",
"status": "draft",
"populated": {
"user": true,
"merchant": false,
"transactions": 1,
"indicators": 1
},
"incomplete": ["user.documents"]
}
],
"failed": [
{
"index": 2,
"id": "CASE-2026-00843",
"error": {
"type": "NOT_FOUND",
"message": "Transaction not found",
"details": ["/transactionIds/0: TXN903 does not exist"]
}
},
{
"index": 3,
"id": "CASE-2026-00844",
"error": {
"type": "VALIDATION_ERROR",
"message": "Request validation failed",
"details": [
"/: at least one of `userId`, `user`, `transactionIds` or `transactions` is required"
]
}
}
],
"summary": {
"received": 4,
"created": 2,
"failed": 2
},
"timestamp": 1754006400789
}Responses
200: Batch processed - inspectcreatedandfailedfor per-item outcomes400: Bad request - the envelope itself was invalid, for examplereportsmissing or over the batch limit. No drafts were created401: Unauthorized403: Reporting not enabled for this client429: Too many requests500: Internal server error
Get Report
Retrieve a single report, including the goAML content Orca has populated and the report’s audit trail.
Endpoint
GET /v1/reports/{reportId}Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
reportId | string | Yes | Orca identifier of the report |
Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
include | string | No | Comma-separated extras to embed: content for the populated goAML content, audit for the change history. Default: neither |
Response Schema
| Field | Type | Description |
|---|---|---|
id | string | Orca report identifier |
status | enum | draft, pending_review, submitted, rejected, resubmission_required, amended, archived |
reportType | enum | STR, SAR, CTR, IFTR, Other |
reportNumber | string | Your internal report number |
fiuReferenceNumber | string | FIC reference number, once filed |
title | string | Report title |
description | string | Report summary |
reason | string | Grounds for the report |
templateId | string | Template used, if any |
indicators | array | goAML indicator codes recorded on the report |
userId | string | Report subject |
transactionIds | array | Transactions attached to the report |
caseOpenedAt | number | When the underlying case was opened |
createdAt | number | Creation timestamp |
createdById | string | Who created the report. api for reports created over this API |
updatedAt | number | Last update timestamp |
updatedById | string | Who last updated the report |
submittedAt | number | When the report was filed with the FIC |
submittedById | string | Dashboard user who filed the report |
content | object | Populated goAML content. Read-only, and present only with include=content |
audit | array | Change history. Present only with include=audit |
content mirrors the goAML report structure, reporting entity, reporting person, reporting
location, transaction with its t_from and t_to parties, account, indicators, reason and action.
It is generated by Orca and cannot be written through the API; the dashboard is the only place it can
be edited.
Example Response
{
"id": "rep_01JQ4T8ZC7K2XN",
"status": "pending_review",
"reportType": "STR",
"reportNumber": "CASE-2026-00841",
"fiuReferenceNumber": null,
"title": "STR: Deposits disproportionate to declared income",
"description": "High movement of deposits against declared monthly income",
"reason": "Subject completed deposits totalling ZAR 1 240 000 over 30 days against a declared monthly income of ZAR 28 000.",
"templateId": null,
"indicators": ["RIND002", "RIND049", "RIND124"],
"userId": "USER123",
"transactionIds": ["TXN123", "TXN124", "TXN125"],
"caseOpenedAt": 1753900000000,
"createdAt": 1754006400123,
"createdById": "api",
"updatedAt": 1754010000000,
"updatedById": "EMP-2841",
"submittedAt": null,
"submittedById": null,
"timestamp": 1754010500000
}Responses
200: Report returned401: Unauthorized403: Reporting not enabled for this client404: Report not found500: Internal server error
Reads of a report are written to the report’s audit trail. Do not expose the response to the report subject or to any customer-facing surface, see Confidentiality.
List Reports
Retrieve reports for your client, most recently created first.
Endpoint
GET /v1/reportsQuery Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
status | string | No | Filter by status. Comma-separated values accepted, for example draft,pending_review. Default: all |
reportType | string | No | Filter by report type. Comma-separated values accepted. Default: all |
userId | string | No | Filter to reports about a single subject |
transactionId | string | No | Filter to reports that include a given transaction |
reportNumber | string | No | Look up by your own internal report number |
createdFrom | number | No | Unix epoch milliseconds, only reports created at or after this time |
createdTo | number | No | Unix epoch milliseconds, only reports created at or before this time |
page | number | No | Page number for pagination (default: 1) |
limit | number | No | Results per page (default: 20, max: 100) |
Response Schema
| Field | Type | Description |
|---|---|---|
reports | array | List of report summaries |
reports[].id | string | Orca report identifier |
reports[].status | enum | Current status |
reports[].reportType | enum | Report type |
reports[].reportNumber | string | Your internal report number |
reports[].fiuReferenceNumber | string | FIC reference number, once filed |
reports[].title | string | Report title |
reports[].userId | string | Report subject |
reports[].transactionCount | number | Number of transactions on the report |
reports[].createdAt | number | Creation timestamp |
reports[].updatedAt | number | Last update timestamp |
reports[].submittedAt | number | Filing timestamp, if filed |
total | number | Total reports matching the query |
page | number | Current page number |
pages | number | Total number of pages |
The list response carries summaries only. Use Get Report with include=content for
the populated goAML content of a single report.
Example Response
{
"reports": [
{
"id": "rep_01JQ4T8ZC7K2XN",
"status": "pending_review",
"reportType": "STR",
"reportNumber": "CASE-2026-00841",
"fiuReferenceNumber": null,
"title": "STR: Deposits disproportionate to declared income",
"userId": "USER123",
"transactionCount": 3,
"createdAt": 1754006400123,
"updatedAt": 1754010000000,
"submittedAt": null
},
{
"id": "rep_01JQ2R1AA9J8ML",
"status": "submitted",
"reportType": "STR",
"reportNumber": "CASE-2026-00792",
"fiuReferenceNumber": "113600000-0-0",
"title": "STR: Transfers between linked accounts",
"userId": "USER456",
"transactionCount": 12,
"createdAt": 1752800000000,
"updatedAt": 1752900000000,
"submittedAt": 1752900000000
}
],
"total": 41,
"page": 1,
"pages": 3,
"timestamp": 1754010500000
}Responses
200: Reports returned400: Bad request: invalid filter or pagination value401: Unauthorized403: Reporting not enabled for this client500: Internal server error