ARM: Authority Request Management

ARM is the authority-to-company family. Free-text is a member of that family, together with the business-relationship (BR) check and the structured requests. The same sealing, the same authority gate, and the same outbound rules cover every member. It runs over the API and the CLI.

How an authority enters

An authority starts an ARM exchange in one of three ways.

01

Business-relationship check

“Do you hold this subject as a customer?” A yes can name the identifiers a later follow-up will use. POST /v1/requests/business-relationship-check

Structured: follows authorityArmEnabled
02

Free-text

A note, with attachments, when no structured profile is the right shape. POST /v1/requests/information (CLI: qp requests create free-text).

Live while ARM is off, the primary path
03

Structured

Transaction history, KYC/CDD, and relationship-typed follow-ups. Includes an identifier-first request when the authority already holds the wallet or the subject, and a follow-up linked to an earlier check with relatesTo.

Unavailable until authorityArmEnabled is on

Structured ARM and authorityArmEnabled

authorityArmEnabled is the CASP service flag that turns structured ARM create and respond on. It is a capability gate for the structured members. Free-text stays in the family either way.

Flag off

Free-text is the primary path and stays available. BR checks, transaction history, KYC, and other structured ARM calls are labelled unavailable until the flag is on. A structured create or respond returns HTTP 403 ARM_STRUCTURED_NOT_ENABLED.

Flag on

Structured create and respond proceed. What the authority receives is then decided by the responder's requestor ruleset. The 403 above is unused in this state.

Discovery

GET /v1/orgs/{orgId}/arm-status returns { "authorityArmEnabled": boolean } for that organisation. Callers use it to see whether structured ARM is on before they choose an entry. The 403 body points here. It is a read of the existing flag, and it is not a new enablement endpoint.

GET /v1/orgs/me/service-enablement returns the caller's own flags, { "firEnabled": boolean, "authorityArmEnabled": boolean }. The developer console reads the same payload through the portal BFF GET /api/developer/org/service-enablement?env=sandbox|prod. Signed-in org admins manage the flag on Service activation.

# Whether a responder has structured ARM on.
curl -sS -H "Authorization: Bearer $REQPORT_API_KEY" \
  "$REQPORT_API_BASE/v1/orgs/synthetic-org/arm-status"

# { "authorityArmEnabled": false }

# The caller's own service flags.
curl -sS -H "Authorization: Bearer $REQPORT_API_KEY" \
  "$REQPORT_API_BASE/v1/orgs/me/service-enablement"

# { "firEnabled": false, "authorityArmEnabled": false }

# Portal BFF (signed-in developer console session):
# GET /api/developer/org/service-enablement?env=sandbox
# GET /api/developer/org/service-enablement?env=prod

How an answer is released

01

The default is HOLD

Every outgoing answer is held for a person unless a rule releases it. The responder's requestor ruleset is evaluated in order. The first match wins. A response that matches nothing stays held.

02

Same-jurisdiction law enforcement can auto-release two answers

A pre-seeded default auto-releases answers from verified law enforcement in the responder's own jurisdiction, for two response types: the business-relationship check, and eligible free-text (a note and attachments). The match uses the requestor's oracle-verified status (isAuthority, lawEnforcementAgency, country) and the response type.

03

Follow-ups stay held

A follow-up on an existing case (transaction history, KYC, or a further free-text ask) is held for a person. The authority sees pending, released, or declined on each item. Held answers are worked with qp pending.

Hard errors

Both codes are stable. Clients match code exactly. Content-Type is application/json.

HTTP 400 ARM_INFORMATION_STRUCTURED_FIELDS

Shape error on a free-text (information) answer that includes accounts, an inline statement or camt.053, or other follow-up-typed structure. The call is refused. The answer is not accepted into HOLD_FOR_APPROVAL. Validation runs before disposition, so the refusal is returned even when same-jurisdiction law enforcement would otherwise auto-release an eligible free-text answer. The same body is returned on POST /v1/requests/{id}/response, the portal composer, qp respond, and MCP reqport_respond. rejectedFields lists the offending top-level names present on that answer. The sample below is accounts. steer.exampleSlug is advisory (transaction-history in v0.1). It is not a create URL.

{
  "code": "ARM_INFORMATION_STRUCTURED_FIELDS",
  "message": "Free-text (information) answers may only include note and attachments. Submit accounts, statement, or camt.053 on the structured follow-up path.",
  "steer": {
    "family": "ARM",
    "use": "structured-follow-up",
    "exampleSlug": "transaction-history"
  },
  "rejectedFields": [
    "accounts"
  ]
}

HTTP 403 ARM_STRUCTURED_NOT_ENABLED

Capability gate, distinct from the shape error above. The organisation's authorityArmEnabled is false, and the caller attempts a structured ARM create or respond: a BR check, a relationship-typed follow-up such as transaction history, KYC, or any other structured member. Free-text create and respond stay available and do not return this code. When the flag is true, this code is unused.

{
  "code": "ARM_STRUCTURED_NOT_ENABLED",
  "message": "Structured ARM create and respond require authorityArmEnabled. Free-text remains available.",
  "steer": {
    "family": "ARM",
    "use": "free-text-or-enable",
    "discovery": "GET /v1/orgs/{orgId}/arm-status"
  }
}

Compatibility

On the first release of this increment, a free-text answer that includes structured fields is a hard HTTP 400. There is no deprecation window. A free-text answer that is only a note and attachments stays a live call.

Schemas

IWDX is the schema source of record. The profiles below are rendered from content/developer/iwdx. This page does not define a second field list.

Sandbox examples

Copy-paste examples for --env sandbox. Every identifier is synthetic. Use your own sandbox key. Do not send these bodies to production.

a Eligible free-text: note and attachments

A free-text member is a note plus attachments. Same-jurisdiction law enforcement can auto-release this answer. The data below is synthetic.

# Eligible free-text. Synthetic sandbox only.
qp --env sandbox requests create free-text \
  --responder synthetic-responder.example \
  --request "Synthetic sandbox note. Please read the attached notice and say whether it matches a record you hold." \
  --case SYN-CASE-1001 \
  --legal-basis "SANDBOX-ONLY"

# Attachment on the free-text graph node.
curl -sS -X POST "$REQPORT_API_BASE/v1/attachments" \
  -H "Authorization: Bearer $REQPORT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "target": { "kind": "node", "id": "SYN-NODE-1001" },
    "filename": "synthetic-notice.txt",
    "mimeType": "text/plain",
    "contentBase64": "c3ludGhldGljIHNhbmRib3ggbm90aWNl"
  }'

b Structured fields on a free-text answer

An information answer that includes accounts, an inline statement, or camt.053 is refused on the answer path. HTTP 400 ARM_INFORMATION_STRUCTURED_FIELDS, with steer. The sample rejectedFields value is accounts. Synthetic ids only.

# Answer path. Synthetic request id.
# Structured fields on a free-text answer are refused, not stored.
qp --env sandbox respond SYN-REQUEST-1002 \
  --status COMP \
  --free-text "Synthetic sandbox note." \
  --account WALLET:synthetic-wallet-001 \
  --yes

c Business-relationship check

A BR check asks whether the synthetic subject is a customer. The subject question is the IWDX business-relationship-check profile. This call is a structured member, so it follows the authorityArmEnabled gate.

curl -sS -X POST "$REQPORT_API_BASE/v1/requests/business-relationship-check" \
  -H "Authorization: Bearer $REQPORT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "responderDomain": "synthetic-responder.example",
    "invstgtnId": "SYN-CASE-1003",
    "legalBasis": "SANDBOX-ONLY",
    "subjectPersonnummer": "SYN-PNR-1003"
  }'

d Structured call while ARM is off

authorityArmEnabled is false. The same structured BR call is refused. Free-text stays available. HTTP 403, code ARM_STRUCTURED_NOT_ENABLED.

# GET /v1/orgs/synthetic-org/arm-status
# { "authorityArmEnabled": false }

curl -sS -X POST "$REQPORT_API_BASE/v1/requests/business-relationship-check" \
  -H "Authorization: Bearer $REQPORT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "responderDomain": "synthetic-responder.example",
    "invstgtnId": "SYN-CASE-1004",
    "legalBasis": "SANDBOX-ONLY",
    "subjectPersonnummer": "SYN-PNR-1004"
  }'

e Structured BR while ARM is on

authorityArmEnabled is true, so the BR create is accepted. Release then follows the requestor ruleset: same-jurisdiction law enforcement auto-releases the BR answer; every follow-up stays held.

# GET /v1/orgs/synthetic-org/arm-status
# { "authorityArmEnabled": true }

curl -sS -X POST "$REQPORT_API_BASE/v1/requests/business-relationship-check" \
  -H "Authorization: Bearer $REQPORT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "responderDomain": "synthetic-responder.example",
    "invstgtnId": "SYN-CASE-1005",
    "legalBasis": "SANDBOX-ONLY",
    "subjectPersonnummer": "SYN-PNR-1005"
  }'
Drive it from the CLI or the API. Responders read a requestor with qp org-states <orgId> and set release rules with qp requestor-ruleset. Held answers are qp pending. Authorities create free-text with qp requests create free-text, and structured members with qp requests create tx-history / kyc, over authority:write. Run qp arm to print the flow.