
# Transaction, behaviour and event monitoring

Send each transaction or customer activity when it happens. Myaza connects it to the customer, evaluates the relevant controls and returns an explainable `allow`, `review` or `block` decision.

## Choose the correct endpoint

| What you are sending | Endpoint | Why |
|---|---|---|
| A payment, transfer, deposit or withdrawal with sender and recipient details | `POST /api/v1/transactions` | Builds the transaction relationship, records accounts or wallets, screens the correct directional party and runs fraud assessment. |
| A login, sign-up, password reset, beneficiary change or other supported customer activity | `POST /api/v1/activities` | Builds Behaviour intelligence from device, IP, location and event attributes, then runs fraud assessment. |
| An existing integration using the older event shape | `POST /api/identity/events` | Compatibility only. It accepts a reduced free-form payload but cannot model complete parties and instruments. |

Use a secret `sk_` key with the same base URL and authentication as the [Identity Hub](https://trust.myaza.co/documentation/identity-hub/markdown#base-url-authentication). Use the v1 endpoints for new integrations.

## Complete value reference

The tables below are the complete closed-value fields accepted by the v1 transaction, activity and outcome contracts. Values are lowercase and case-sensitive in requests. If a value is not listed here, the API rejects it rather than guessing.

### Request enum values

| Field | All accepted values |
|---|---|
| `subject.type` | `individual`, `business`, `wallet`, `bank_account`, `device`, `email`, `phone`, `ip` |
| Activity `type` | `login`, `signup`, `password_reset`, `beneficiary_added`, `profile_change`, `device_change`, `payout`, `withdrawal` |
| `transaction.assetClass` | `fiat`, `crypto` |
| `transaction.direction` | `inbound`, `outbound` |
| `transaction.parties[].role` | `customer`, `sender`, `recipient`, `counterparty` |
| `transaction.parties[].type` | `individual`, `business`, `unknown` |
| `transaction.parties[].instruments[].type` | `bank_account`, `crypto_wallet`, `card`, `other` |
| Outcome `classification` | `fraud`, `legitimate`, `inconclusive` |
| Outcome `source` | `customer`, `analyst`, `chargeback`, `case`, `sar`, `automation` |

The response decision values are `allow`, `review` and `block`. Use them as machine values and show your own customer-facing copy separately.

### Open string fields

These fields are intentionally not enums: `channel`, `transaction.transactionType`, `transaction.status`, `transaction.paymentMethod`, `transaction.blockchain`, `currency`, `metadata` keys and `customAttributes` keys. Send stable, documented values from your own system. Myaza stores them as evidence and may use them in authorised custom controls, but does not silently translate them into another value.

`POST /api/identity/events` is a compatibility endpoint, so its `type` is also an open string. Recognised names containing transaction, payment, transfer, login, sign-in, sign-up, register, password reset, beneficiary added, profile change, device change, payout or withdrawal are routed into the canonical assessment pipeline. New integrations should use the closed v1 activity types above.

## Send a complete transaction

`POST /api/v1/transactions`

The `subject` is your customer. The `parties` array describes who sent and received the funds. Add each bank account, wallet, card or other payment method to the party that used it.

### Outbound bank transfer

```json
{
  "externalActivityId": "activity_txn_10001",
  "subject": {
    "type": "individual",
    "externalUserId": "user_42"
  },
  "occurredAt": "2026-08-16T09:30:00.000Z",
  "channel": "bank_transfer",
  "deviceRef": "device_7f53",
  "ipAddress": "102.89.12.34",
  "country": "NG",
  "location": {
    "city": "Lagos",
    "region": "Lagos",
    "latitude": 6.5244,
    "longitude": 3.3792,
    "accuracyRadiusKm": 10
  },
  "transaction": {
    "externalTransactionId": "txn_10001",
    "assetClass": "fiat",
    "direction": "outbound",
    "amount": "48000.00",
    "currency": "NGN",
    "transactionType": "bank_transfer",
    "status": "pending",
    "channel": "bank_transfer",
    "paymentMethod": "bank_account",
    "parties": [
      {
        "role": "sender",
        "externalUserId": "user_42",
        "type": "individual",
        "displayName": "Sample customer",
        "country": "NG",
        "deviceRef": "device_7f53",
        "instruments": [
          {
            "type": "bank_account",
            "externalInstrumentId": "customer_account_01",
            "identifier": "0123456789",
            "institutionName": "Sample Bank",
            "country": "NG",
            "currency": "NGN",
            "verified": true
          }
        ]
      },
      {
        "role": "recipient",
        "externalPartyId": "beneficiary_998877",
        "type": "individual",
        "displayName": "Amina Bello",
        "country": "NG",
        "instruments": [
          {
            "type": "bank_account",
            "externalInstrumentId": "beneficiary_account_998877",
            "identifier": "9988776655",
            "institutionName": "Recipient Bank",
            "country": "NG",
            "currency": "NGN"
          }
        ]
      }
    ],
    "metadata": {
      "paymentPurpose": "supplier_payment",
      "customerReference": "invoice_2026_104"
    }
  }
}
```

This appears as **Sample customer → Amina Bello** throughout the dashboard. For an inbound transaction, the customer is the recipient and the title appears as **Sample customer ← sender name**.

### Inbound crypto transfer

```json
{
  "externalActivityId": "activity_crypto_10002",
  "subject": {
    "type": "individual",
    "externalUserId": "user_42"
  },
  "occurredAt": "2026-08-16T10:05:00.000Z",
  "channel": "blockchain",
  "country": "NG",
  "transaction": {
    "externalTransactionId": "crypto_10002",
    "assetClass": "crypto",
    "direction": "inbound",
    "amount": "0.25",
    "currency": "USDT",
    "transactionType": "transfer",
    "blockchain": "ethereum",
    "parties": [
      {
        "role": "sender",
        "externalPartyId": "crypto_sender_01",
        "type": "individual",
        "displayName": "Charles Okafor",
        "country": "NG",
        "instruments": [
          {
            "type": "crypto_wallet",
            "externalInstrumentId": "sender_wallet_01",
            "identifier": "0x8ba1f109551bd432803012645ac136ddd64dba72",
            "network": "ethereum",
            "currency": "USDT"
          }
        ]
      },
      {
        "role": "recipient",
        "externalUserId": "user_42",
        "type": "individual",
        "displayName": "Sample customer",
        "country": "NG",
        "instruments": [
          {
            "type": "crypto_wallet",
            "externalInstrumentId": "customer_wallet_01",
            "identifier": "0x742d35cc6634c0532925a3b844bc454e4438f44e",
            "network": "ethereum",
            "currency": "USDT",
            "verified": true
          }
        ]
      }
    ]
  }
}
```

This appears as **Sample customer ← Charles Okafor**. Myaza screens the sender's crypto wallet before producing the transaction decision.

## How directional screening works

The transaction direction is always from the perspective of your customer.

| Direction and asset | Party screened before the decision | What is sent for screening |
|---|---|---|
| Outbound fiat | Recipient | The recipient's name and available identity evidence. Bank account numbers remain relationship evidence and are not used for screening. |
| Inbound fiat | Sender | The sender's name and available identity evidence. Bank account numbers remain relationship evidence and are not used for screening. |
| Outbound crypto | Recipient | The recipient wallet address and network. |
| Inbound crypto | Sender | The sender wallet address and network. |

Individuals are checked for sanctions, PEP and adverse media. Businesses are checked for sanctions and adverse media. If the required target name or wallet address is missing, Myaza returns a review decision with an insufficient-data reason rather than treating the transaction as clear.

## Transaction fields

### Request envelope

| Field | Required | Meaning |
|---|---:|---|
| `externalActivityId` | Yes | Stable idempotency key for this assessment. Reuse it only when retrying the same logical request. |
| `subject` | Yes | Your customer. Supply exactly one of `subject.id` or `subject.externalUserId`. |
| `occurredAt` | Yes | ISO 8601 time when the activity happened. It cannot be more than five minutes in the future. |
| `channel` | No | Origin such as `bank_transfer`, `mobile_app`, `card` or `blockchain`. |
| `deviceRef` | No | Your stable device reference. Reusing it builds relationship and new-device evidence. |
| `ipAddress` | No | Valid end-user IPv4 or IPv6 address. Myaza does not substitute the calling server's IP address. |
| `country` | No | Two-letter country code associated with the event. |
| `location` | No | `city`, `region`, coordinates and optional accuracy radius. Latitude and longitude must be supplied together. |
| `metadata` | No | Up to 50 flat fields. Values may be strings, numbers, booleans, nulls or short primitive arrays. |
| `customAttributes` | No | Up to 50 flat custom values available to authorised custom controls. |

### `transaction`

| Field | Required | Meaning |
|---|---:|---|
| `externalTransactionId` | Yes | Your stable transaction reference. It appears in APIs, evidence and webhooks. |
| `assetClass` | Yes | `fiat` or `crypto`. This selects the directional screening type. |
| `direction` | Yes | `inbound` or `outbound`, from your customer's perspective. |
| `amount` | Yes | Positive decimal string with up to eight decimal places. Send a string to preserve exact money. |
| `currency` | Yes | Fiat currency or asset code, such as `NGN`, `USD`, `USDT` or `BTC`. |
| `transactionType` | Yes | Your transaction category, such as `bank_transfer`, `card_payment`, `deposit` or `withdrawal`. |
| `status`, `channel`, `paymentMethod` | No | Operational context used in evidence and custom controls. |
| `blockchain` | Crypto only | Network for a crypto transaction, such as `ethereum` or `bitcoin`. |
| `parties` | Recommended | Structured sender and recipient records. Supply these to unlock transaction titles, relationship intelligence and directional screening. |
| `metadata` | No | Flat transaction-specific values such as purpose or your own customer reference. |

### Party fields

| Field | Required | Meaning |
|---|---:|---|
| `role` | Yes | `sender`, `recipient`, `customer` or `counterparty`. Use sender and recipient for a complete money movement. |
| `externalUserId` | One stable reference | Use when this party is already an entity in your Myaza environment. |
| `entityId` | One stable reference | Myaza entity ID. Prefer `externalUserId` when integrating from your own system. |
| `externalPartyId` | One stable reference | Your durable reference for an external counterparty that is not yet an entity. |
| `displayName` | Required for external fiat screening | Human-readable person or business name. It also becomes the dashboard transaction title. |
| `type` | No | `individual`, `business` or `unknown`. It selects the relevant name-screening checks. |
| `country`, `industry` | No | Counterparty context for geography, screening and relationship evidence. |
| `deviceRef` | No | Device used by this party, when your system observes it. |
| `instruments` | No | Bank accounts, crypto wallets, cards or other payment methods used by this party. |

Every party needs at least one stable reference: `externalUserId`, `entityId` or `externalPartyId`.

### Instrument fields

| Field | Required | Meaning |
|---|---:|---|
| `type` | Yes | `bank_account`, `crypto_wallet`, `card` or `other`. |
| `externalInstrumentId` | One stable value | Your durable account, wallet or payment-method reference. |
| `identifier` | One stable value | Raw account number or wallet address observed for this transaction. It is fingerprinted for relationship matching and is not stored in raw form. |
| `fingerprint` | One stable value | An existing service or device token when raw identifiers should not be submitted. |
| `maskedIdentifier` | No | Safe display value. It cannot identify an instrument on its own. |
| `institutionName`, `country`, `currency` | No | Bank or payment-method context. |
| `network` | Crypto only | Blockchain network used by the wallet. |
| `verified` | No | Whether your authorised process verified that the party controls this instrument. |

Each instrument needs `externalInstrumentId`, `identifier` or `fingerprint`. Myaza derives a tenant-scoped fingerprint and keeps only safe display evidence.

## Transaction assessment response

The response wraps the persisted decision in `assessment`. Read `summary` first:

```json
{
  "assessment": {
    "activityId": "act_01j9ghi000",
    "transactionId": "atx_01j9ghi001",
    "decisionId": "dec_01j9ghi002",
    "decision": "review",
    "summary": {
      "outcome": "review",
      "title": "Review required",
      "reason": "New beneficiary with value matched.",
      "score": 72,
      "riskLevel": "high",
      "matchedRules": 1,
      "nextAction": {
        "type": "review_activity",
        "label": "Review transaction"
      }
    },
    "alertId": "alt_01j9ghi003",
    "assessedAt": "2026-08-16T09:30:00.284Z"
  }
}
```

Use `summary.outcome` to gate your flow and `summary.nextAction` to continue it. The complete response also retains matched rules, screening, billing, signals and processing evidence when you need to audit or investigate the result.

Production billing uses the component prices configured for your organisation. A transaction can contain a fraud-assessment line and one or more screening lines. Sandbox requests are non-billable. Invalid input is rejected before usage is reserved, and service failures are not treated as successful billable assessments.

## Send behaviour activity

`POST /api/v1/activities`

Use this endpoint for non-transaction customer behaviour. The example below connects a login to the customer, device, IP and location used at the time.

```json
{
  "externalActivityId": "login_10001",
  "subject": {
    "type": "individual",
    "externalUserId": "user_42"
  },
  "type": "login",
  "occurredAt": "2026-08-16T09:30:00.000Z",
  "channel": "mobile_app",
  "deviceRef": "device_7f53",
  "ipAddress": "102.89.12.34",
  "country": "NG",
  "location": {
    "city": "Lagos",
    "region": "Lagos",
    "latitude": 6.5244,
    "longitude": 3.3792,
    "accuracyRadiusKm": 10
  },
  "metadata": {
    "authenticationMethod": "password",
    "sessionReference": "session_20001"
  },
  "customAttributes": {
    "trustedDevice": false,
    "failedAttemptsBeforeSuccess": 2
  }
}
```

### Supported behaviour types

| Type | Use it for |
|---|---|
| `login` | Successful or attempted customer login. |
| `signup` | Account registration activity. |
| `password_reset` | Password or credential recovery. |
| `beneficiary_added` | A new payee or beneficiary. |
| `profile_change` | Material customer-profile changes. |
| `device_change` | A device replacement or trust-state change. |
| `payout` | Non-transaction payout activity that does not carry full sender and recipient details. |
| `withdrawal` | Non-transaction withdrawal activity that does not carry full transaction details. |

Use `POST /api/v1/transactions` when money movement has sender, recipient, account or wallet evidence. Do not send a transaction through the generic activity endpoint.

Each accepted activity updates the customer's Behaviour intelligence with the evidence supplied, evaluates active risk-scoring rules, and records the resulting decision. Location observations appear under **Connected records → Locations & IPs**. Device references contribute to device history and relationship evidence.

## Location and IP evidence

Send only evidence you actually collected. `ipAddress` must be a valid IPv4 or IPv6 address, `country` uses a two-letter code, and latitude and longitude must be supplied together. `accuracyRadiusKm` is optional and is accepted only with coordinates.

If you send both location and IP evidence, your supplied location remains primary. IP enrichment fills only missing fields. Replaying the same logical activity does not create a duplicate location observation.

Location capture happens before billable fraud assessment. Invalid activity input does not create a location record or an assessment charge. A temporary location-enrichment failure does not turn otherwise valid activity into a chargeable failure.

## Idempotency and freshness

- Use one `externalActivityId` per logical assessment and keep it stable across retries.
- Use one `externalTransactionId` per customer transaction.
- Replaying the same request returns the existing assessment with `replayed: true` and does not score or bill it again.
- Reusing either identifier with different immutable transaction data returns a conflict.
- Send the original `occurredAt`. Myaza retains both the occurrence time and receipt time so late events remain visible and do not distort the audit trail.

## Webhooks

Subscribe to `fraud.transaction.assessed` for every transaction decision and `fraud.activity.assessed` for every behaviour decision. Transaction webhooks include the subject, external activity and transaction references, structured parties, masked instruments, screening evidence and billing breakdown. Use `event.flagged` only as a compatibility alias for non-allow decisions.

See [Transaction Monitoring webhooks](https://trust.myaza.co/documentation/webhook-transaction-monitoring/markdown) and [Event Monitoring webhooks](https://trust.myaza.co/documentation/webhook-event-monitoring/markdown) for delivery contracts and retry guidance.

## Compatibility event endpoint

`POST /api/identity/events` remains available for existing integrations:

```json
{
  "externalUserId": "user_42",
  "externalEventId": "txn_10001",
  "type": "transaction.transfer",
  "data": {
    "amount": 48000,
    "currency": "NGN",
    "country": "NG",
    "counterparty": "beneficiary_998877",
    "direction": "outbound",
    "channel": "bank_transfer",
    "ipAddress": "102.89.12.34",
    "location": {
      "city": "Lagos",
      "region": "Lagos",
      "latitude": 6.5244,
      "longitude": 3.3792,
      "accuracyRadiusKm": 10
    }
  }
}
```

This reduced shape can store and score a simple event, but it cannot submit structured sender and recipient profiles or attach instruments to each party. Migrate transaction integrations to `POST /api/v1/transactions` to receive complete relationship intelligence, directional screening, billing evidence and human-readable transaction titles.

## Errors

| Status | Error | Meaning |
|---|---|---|
| `400` | `invalid_request` | Required fields are missing, money is not an exact decimal string, location is incomplete or the payload contains unsupported nested values. |
| `402` | `insufficient_wallet` | The production credit balance cannot reserve the configured assessment components. |
| `404` | `subject_not_found` or `party_not_found` | The supplied customer or linked entity does not exist in this organisation and environment. |
| `409` | `idempotency_conflict` or `transaction_id_conflict` | A stable identifier was reused for different immutable data. |
| `422` | `subject_type_not_supported` | The subject cannot be assessed by this activity contract. |
| `503` | Service failure | Assessment did not complete. Follow normal retry guidance using the same identifiers. |

## Next steps

- [Fraud rules](https://trust.myaza.co/documentation/transaction-rules/markdown): install or build controls for transactions, logins and other activity.
- [Ongoing monitoring](https://trust.myaza.co/documentation/monitoring-policies/markdown): configure recurring customer review.
- [Investigations and reporting](https://trust.myaza.co/documentation/investigations/markdown): investigate review and block decisions.
- [Webhooks](https://trust.myaza.co/documentation/webhooks/markdown): receive lifecycle events without polling.
