Aller au contenu

Reports API

Ce contenu n’est pas encore disponible dans votre langue.

The Reports API is what the Reports screens use: the register of STRs and CTRs, drafting an STR from a case, four-eyes approval, the goAML XML export, and the record of what was filed with the FIC and when. Read Regulatory reporting first for how the process works.

Who can call it. Signed-in people only; an API key gets 401 sign in required. Someone who hasn’t finished setting up their account (two-factor or a new password) gets 403.

Two permissions on every call. Reports hold customers’ identity in full, so every endpoint below except the settings needs entities.view_pii and at least one of reports.draft, reports.approve or reports.submit, plus the permission named for the action. With the default roles, senior analysts, L2 investigators and MLROs can use it.

Everything is audited. Opening a report (at most once per person and report every 30 minutes), editing, every status change, the export, every download and settings changes are written to the audit log under your name.

Signing in with curl. Sign in once and keep the session cookie, as described in Alerts and cases API: before you start. Then add -b ~/.sp-cookies to every request below.

Endpoint Does Needs (as well as entities.view_pii) Success
GET /api/v1/reports The register, newest first Any reporting permission 200
GET /api/v1/reports/counts How many reports in each status Any reporting permission 200
GET /api/v1/reports/{id} One report, with its subject, transactions and validation Any reporting permission 200
POST /api/v1/cases/{id}/reports/str Draft an STR from a case, or return the one in progress reports.draft 200 {"id", "created"}
PATCH /api/v1/reports/{id} Edit a draft reports.draft 204
POST /api/v1/reports/{id}/request-approval Send a draft for approval reports.draft 204
POST /api/v1/reports/{id}/return Send a report back to draft reports.approve 204
POST /api/v1/reports/{id}/approve Approve (not one you drafted or sent) reports.approve 204
POST /api/v1/reports/{id}/void Void a report that must not be filed, with a reason reports.approve 204
POST /api/v1/reports/{id}/export Make the goAML XML, once reports.approve or reports.submit 200
GET /api/v1/reports/{id}/download The exported file reports.approve or reports.submit 200, the XML
GET /api/v1/reports/{id}/package The filing package: the export, the chosen evidence files and a manifest, as one ZIP reports.approve or reports.submit 200, a ZIP
POST /api/v1/reports/{id}/submission Record the upload to goAML reports.submit 204
POST /api/v1/reports/{id}/outcome Record the FIC’s acknowledgement or rejection reports.submit 204
GET /api/v1/settings/reporting The reporting settings settings.manage alone is enough; otherwise any reporting permission 200
PUT /api/v1/settings/reporting Replace the reporting settings settings.manage alone, or reports.approve 200
Query parameter Meaning
status draft, pending_approval, approved, submitted, acknowledged, rejected or voided. Empty for all
type str, ctr or sar. Empty for all
case_id Only reports drafted from this case (a UUID)
q Part of the report number, title, the FIU’s reference or case number (up to 128 characters)
limit 1 to 200, default 50
cursor The Next-Cursor header of the previous page

Each report in the list:

Field Meaning
id, number, type, status, title The report. number is like STR-2026-000014 or CTR-2026-000031
source case (an STR drafted from a case) or ctr_job (a CTR drafted automatically)
business_day CTRs: the day, YYYY-MM-DD
case_id, case_number STRs: the case it was drafted from
regulator_ref, submitted_at Once submitted: the FIC’s reference and when it was uploaded
created_at, updated_at When it was made and last changed
created_by_name Who drafted it. Absent for CTRs drafted by the system
transaction_count, amount_base_minor How many transactions are selected, and their total in the base currency’s minor units

When there are more, the response has a Next-Cursor header; pass it as cursor for the next page. A bad cursor gets 422 cursor is invalid; start again without it.

Terminal window
# Reports waiting for approval
curl -s -b ~/.sp-cookies "$SP/api/v1/reports?status=pending_approval"

An object of status to count, for example {"draft": 3, "pending_approval": 1, "submitted": 12}. Statuses with no reports are left out.

Everything in the list, plus:

Field Meaning
jurisdiction, format The workspace’s country, and the report format from the settings: goaml, or centif-uemoa (the CENTIF declaration)
fiu The short name of the FIU the workspace’s jurisdiction reports to, such as FIC, NFIU or CENTIF-CI. For an approved report, the FIU of the jurisdiction it was approved under. The app uses it wherever a report’s page names the FIU: the status, the reference, the hints and the submission and outcome panels
report_code The FIU’s report code from the settings, such as STR (or DOS for a CENTIF)
reason, action_taken, indicators The narrative, the action taken and the indicator codes
attachments The titles of the documents filed with the report, in order. Frozen with the rest of the content once the report leaves draft
files The case’s evidence files for this report, oldest first, each with id, file_name, size_bytes, sha256 (hex) and selected. On a draft made from a case: every file still on the case, with selected saying which go with the report. Once the report has left draft: only the chosen files, all selected: true. Empty for a report without a case (a CTR) or a case without files. Frozen with the rest of the content once the report leaves draft
total_base_minor CTRs: the cash total recorded by the CTR job
subject Who it is about: type, id, name, external_id, identity (an object of decrypted details, such as "Phone", "ID (ghana card)", "Date of birth", "Account") and designations (active designation types)
transactions The transactions the report may include, each with id, external_id, occurred_at, direction, type, channel, status, amount_minor, currency, amount_base_minor, counterparty, account and selected. For an STR: the case’s alert transactions and any already selected, newest first, at most 500
problems Validation of the report as it would be exported now: each with path, message and severity (error blocks, warning doesn’t)
can_void true when you may void it now: it is a draft, awaiting approval or approved, and you hold reports.approve
can_approve true when you may approve it now: it is awaiting approval, you hold reports.approve, and you didn’t draft it or send it for approval
created_by, requested_by, requested_by_name, requested_at Who drafted it, and who sent it for approval and when
approved_by_name, approved_at, submitted_by_name Who approved and when, and who recorded the submission
outcome_by_name, outcome_at, outcome_note Who recorded the FIC’s answer, when, and the note
voided_by_name, voided_at, void_reason For a voided report: who voided it, when and why
export Once exported: file_name, sha256 (hex), size_bytes, schema_version (goAML 4.0 report schema, or SpeculaPro CENTIF declaration 1.0 (DOS/DTE; XML and printable form; layout to be confirmed with the CENTIF)), exported_at, exported_by

No body. Drafts an STR from the case, pre-filled as described in Drafting an STR from a case, adds a report_linked event to the case timeline, and returns:

{ "id": "0192f4a1-…", "created": true }

If the case already has an STR that is a draft, awaiting approval or approved, nothing is created: that report’s id comes back with "created": false. After a report is submitted, acknowledged, rejected or voided, a new one is drafted.

Drafts only. Send only what you change:

Field Rules Meaning
title Up to 200 characters The register title (not sent to the FIC)
reason Up to 4,000 characters The narrative (goAML reason)
action_taken Up to 4,000 characters goAML action
indicators Up to 50 codes The full list of indicator codes. Duplicates and blanks are dropped
attachments Up to 50 titles The full list of attachment titles (pièces jointes), in order; an empty list clears it. Each is trimmed and cut at 200 characters, and blanks are dropped. Titles only: no file is uploaded. The CENTIF declaration prints them; the goAML file doesn’t carry them
transaction_ids Up to 500 IDs The full selection. Each must be one of the report’s transactions; any not listed is removed
reason See the row above. A narrative the AI assistant drafted is saved through this field like any other text; asking for a draft never changes the report
file_ids Up to 50 IDs The full selection of the case’s evidence files that go with the report; any not listed is removed, and an empty list clears it. Leave the field out to keep the selection. Each must be a file of the report’s own case that hasn’t been removed, and together they may add up to at most 100 MB, or the request fails with 422 and nothing changes. The files come in the filing package; they don’t change the exported report and are separate from attachments
Terminal window
curl -s -b ~/.sp-cookies -X PATCH -H 'Content-Type: application/json' \
-d '{"action_taken":"Wallet restricted pending review","indicators":["MULE1"]}' \
"$SP/api/v1/reports/0192f4a1-0000-7000-8000-000000000001"

POST /api/v1/reports/{id}/request-approval

Section titled “POST /api/v1/reports/{id}/request-approval”

No body. Sends a draft for approval. Refused with 422 while the report has validation errors.

Field Required Rules
note Yes 3 to 2,000 characters: what the drafter should change

Sends a report awaiting approval back to draft. The note is kept in the audit log.

Field Required Rules
note No Up to 2,000 characters

Approves a report awaiting approval. Refused with 403 if you drafted it or sent it for approval, and with 422 while it has validation errors. After approval the report’s content can never change, and the reporting settings in force at that moment are kept with it: the export uses them, whatever changes later.

Field Required Rules
reason Yes 3 to 2,000 characters: why the report must not be filed

Needs reports.approve. Voids a draft, a report awaiting approval or an approved report (for example one approved by mistake, a duplicate, or the wrong subject). It answers 204, and the report stays in the register with status voided, voided_by_name, voided_at and void_reason, audited as report.void. A new STR can then be drafted from the case. A submitted report can’t be voided (409 conflict: a submitted report can't be voided). An export already made stays stored, but can’t be recorded as submitted.

No body. For an approved report: builds the file in the report’s format (goAML XML, or the CENTIF declaration archive: a ZIP holding the declaration as XML, the same declaration as a print-ready French HTML form, and the XML’s schema) from the report and the current settings, validates it, re-reads the XML to prove it is well-formed, stores the exact bytes encrypted with the workspace’s key, and returns the export record. A report that has already been exported returns its existing export, whatever its status: the file is made once.

The exported file, exactly as stored, after checking it against its SHA-256. Headers:

Header Value
Content-Type application/xml; charset=utf-8 (goAML), or application/zip (CENTIF declaration)
Content-Disposition attachment; filename="STR-2026-000014.xml", or DOS-2026-000003-centif.zip for the CENTIF archive
X-Content-SHA256 The file’s SHA-256, in hex
Cache-Control no-store
Terminal window
curl -s -b ~/.sp-cookies -OJ "$SP/api/v1/reports/0192f4a1-0000-7000-8000-000000000001/download"

Every download is audited. A report that hasn’t been exported gets 404 not found: this report has not been exported.

The filing package of an exported report, as one ZIP put together at each call:

  • the exported file, exactly as stored, after checking it against its SHA-256;
  • each evidence file chosen for the report (files with selected: true), decrypted and checked against the SHA-256 recorded when it was added. A file whose name is already used in the ZIP gets a number in front, such as 2-statement.pdf;
  • MANIFEST.txt: a heading, then one line for each file as <sha256> <size> bytes <name>.

A report with no chosen files still gets a package: the export and the manifest.

Header Value
Content-Type application/zip
Content-Disposition attachment; filename="STR-2026-000014-package.zip": the export’s name without its extension, then -package.zip (so DOS-2026-000003-centif-package.zip for a CENTIF archive)
X-Content-SHA256 The SHA-256 of the ZIP that was sent, in hex
Cache-Control no-store
Terminal window
curl -s -b ~/.sp-cookies -OJ "$SP/api/v1/reports/0192f4a1-0000-7000-8000-000000000001/package"

Needs reports.approve or reports.submit, and entities.view_pii. Every download is audited as report.package_download, with the export’s SHA-256 and the names of the evidence files in the package. A report that hasn’t been exported gets 404 not found: this report has not been exported. If a stored file doesn’t match its SHA-256 the call fails with 500 and nothing is sent. A support session can’t call it.

Field Required Rules Meaning
regulator_ref Yes 1 to 100 characters The reference goAML gave the submission
submitted_at Yes An RFC 3339 time, not in the future When it was uploaded

For an approved, exported report. The report becomes submitted; the reference and time can never change.

Terminal window
curl -s -b ~/.sp-cookies -H 'Content-Type: application/json' \
-d '{"regulator_ref":"GH-FIC-2026-118734","submitted_at":"2026-03-24T10:15:00Z"}' \
"$SP/api/v1/reports/0192f4a1-0000-7000-8000-000000000001/submission"
Field Required Rules
status Yes acknowledged or rejected
note For rejected Up to 2,000 characters: the FIC’s reason

For a submitted report. Either outcome is final.

GET /api/v1/settings/reporting and PUT /api/v1/settings/reporting

Section titled “GET /api/v1/settings/reporting and PUT /api/v1/settings/reporting”

GET returns the settings, to anyone with settings.manage, settings.view or a reporting permission; PUT replaces them all and returns what was stored. Changing them needs settings.manage or reports.approve; each change is audited as reporting.settings with the values before and after. What each one means: Reporting settings.

Field Type Notes
format String goaml or centif-uemoa. The default is your jurisdiction’s; change it only if your FIU tells you to. It applies to reports drafted from then on: a report keeps the format it was drafted in
fiu, fiu_name, format_name Strings Read-only, worked out from the workspace’s jurisdiction each time the settings are read and never stored; PUT ignores them. fiu is the FIU’s short name (FIC, NFIU, CENTIF-CI; FIU where the jurisdiction names none of its own, as for The Gambia and Other country), fiu_name its full name (Financial Intelligence Centre), and format_name the format in words (goAML or CENTIF declaration). The app uses them to name the FIU and format on the Reports pages
rentity_id Integer The reporting entity ID. Must be set before ctr.enabled
rentity_branch String Up to 255
submission_code String E or M
institution_name, institution_code, swift Strings Up to 255, 50 and 11. swift must be a valid BIC
non_bank_institution Boolean
report_codes Object str, ctr: up to 20 characters each
reporting_person Object title, first_name, last_name, email, phone, occupation
location Object type (default B), address (up to 100), town, city, zip (up to 10), country (two letters), state
indicators Array Up to 500 of {"code", "label"}
typology_indicators Object Rule typology, category or key → array of indicator codes
require_indicators Boolean STRs need at least one indicator
codes Object funds_cash, funds_account, default_transmode, contact_type, mobile_comm_type, signatory_role (strings); transmode, id_types, account_status (objects of SpeculaPro value → FIC code)
ctr Object enabled, threshold_individual_minor and threshold_business_minor (minor units of the base currency, so GHS 50,000 is 5000000 and XOF 15,000,000 is 15000000; 0 means not set), comparison (gte: cash at or above the threshold is reported; gt: only cash above it. Default: the jurisdiction’s), aggregate, cash (up to 50 of {"channel", "type"}), lookback_days (1 to 31). Read only: thresholds_kept_from, a currency code set by an upgrade when the thresholds are that currency’s figures read in the base currency; cleared when a PUT changes either threshold
warnings Array, read only Problems with the settings as they stand, worked out when they are read and never stored. ctr_no_threshold: CTR drafting is on but no threshold is set, so nothing is drafted. ctr_threshold_currency_mismatch: CTR drafting is on against thresholds an upgrade kept from another currency (ctr.thresholds_kept_from names it), so almost nothing is drafted; it clears only when a PUT changes threshold_individual_minor or threshold_business_minor; a PUT that leaves both as they are keeps it. Leave warnings out of a PUT

Fields you leave out are filled with their defaults. Values you send are checked as sent:

You send Answer
A threshold below zero 422 invalid: CTR thresholds can't be negative (0, or left out, means not set)
lookback_days outside 1 to 31 422 invalid: CTR lookback must be 1 to 31 days
A comparison other than gte or gt 422: only those two values are accepted
ctr.enabled: true while either threshold is unset 422 invalid: set the CTR thresholds for individuals and businesses before enabling CTRs (this workspace's jurisdiction defines none)

A threshold of 0, or left out, takes the jurisdiction’s figure. Where the jurisdiction defines none (Other country, global), it stays 0: no CTR is drafted and ctr.enabled is refused until you send both thresholds. Settings can’t be saved that way, but a workspace can end up there when the jurisdiction it relied on for its threshold stops applying; warnings then carries ctr_no_threshold. See When no threshold is set.

Status Meaning Examples
401 Not signed in, or called with an API key sign in required
403 A permission is missing, or four eyes forbidden: reports contain customer identity; you need entities.view_pii; forbidden: missing permission reports.draft; forbidden: you need a reporting permission; forbidden: you drafted this report or sent it for approval; someone else must approve it; forbidden: reports.approve or reports.submit is needed to export; missing permission settings.manage or reports.approve
404 No such report or case in your workspace not found; not found: this report has not been exported
409 The report isn’t in the right status conflict: only drafts can be edited (this report is pending_approval); conflict: report is approved; conflict: nothing awaiting approval; conflict: only approved reports are exported (this one is draft); conflict: only approved reports can be recorded as submitted (this one is submitted); conflict: export the file before recording the submission; conflict: only submitted reports get an outcome (this one is approved); conflict: a submitted report can't be voided
422 Validation invalid: fix the report first: rentity_id: is required: … (up to five problems, then and N more); invalid: transaction <id> is not one of this report's transactions; invalid: file <id> is not one of the case's evidence files; invalid: the submission date can't be in the future; invalid: say why the FIU rejected it; invalid: the reference number the FIU gave the filing is required; invalid: say why the report is voided; case_id must be a UUID; cursor is invalid; start again without it; settings: invalid: "ABC" is not a SWIFT/BIC code, invalid: indicator "X" is listed twice, invalid: typology "mule" maps to "M9", which is not in the indicator list, invalid: unknown channel "x" in transmode codes, invalid: set the reporting entity id before enabling CTRs, invalid: say which transactions count as cash before enabling CTRs