> ## Documentation Index
> Fetch the complete documentation index at: https://docs.didit.me/llms.txt
> Use this file to discover all available pages before exploring further.

# Submitting Transactions

> Submit transactions for real-time monitoring with parties, payment methods, device context, and custom properties. Pay-per-call $0.02 per transaction.

Submit transactions to Didit via the [Create Transaction](/management-api/transactions/create) API. Each transaction includes structured party, payment, and context data that the rules engine evaluates in real time.

<Tip>
  When the transaction originates on an end-user's device - a crypto withdrawal, an in-app payment - consider [SDK transaction submission](/transaction-monitoring/sdk-transaction-submission): a scoped token replaces the API key, device intelligence is collected automatically, and required user actions (wallet-ownership proof, step-up verification) launch on the spot.
</Tip>

## Transaction categories

Every transaction has a `transaction_category` that determines how rules and analytics treat it:

| Category | Description |
| - | - |
| `finance` | Standard financial transaction (deposit, withdrawal, transfer) |
| `kyc` | Identity verification-related event |
| `travel_rule` | Virtual asset transfer requiring travel rule compliance |
| `user_event` | Custom platform event (login, profile change, etc.) |
| `gambling_bet` | Gambling wager or bet placement |
| `gambling_limit_change` | Change to a player's deposit, loss, or wager limit |
| `gambling_bonus_change` | Bonus grant, redemption, or forfeiture |
| `audit_trail_event` | System or compliance audit event |

## Transaction details

The `transaction_details` object carries the financial data:

| Field | Type | Description |
| - | - | - |
| `direction` | string | `inbound` or `outbound` |
| `amount` | string | Transaction value as a decimal string (e.g., `"1500.50"`) |
| `currency` | string | ISO currency code (e.g., `USD`, `EUR`, `BTC`) |
| `currency_kind` | string | `fiat` or `crypto` — determines whether crypto screening triggers |
| `action_type` | string | Vendor-defined action label (e.g., `deposit`, `withdrawal`, `purchase`) |
| `payment_details` | string | Free-text description of the payment (e.g., `"Bank transfer from ABC Corp"`) |
| `payment_reference_id` | string | External payment reference identifier. For post-transfer crypto screening, use the blockchain transaction hash. |

## Subject and counterparty

Each transaction has a **subject** (required) and an optional **counterparty**. Both use the same party model:

| Field | Type | Description |
| - | - | - |
| `entity_type` | string | `person` or `company` |
| `vendor_data` | string | Your identifier for this party — links the transaction to a [user](/management-api/users/list) or [business profile](/business-verification/business-profiles) |
| `full_name` | string | Full name of the party |
| `first_name` | string | Given name |
| `last_name` | string | Family name |
| `date_of_birth` | string | ISO date (e.g., `1990-01-15`) |
| `country_code` | string | ISO 3166-1 alpha-2 country code |
| `address` | object or string | Structured address with `street`, `city`, `state`, `country`, `postal_code`. Also accepts a plain string, such as a wallet address for a crypto counterparty — Didit stores it unchanged on the party. |

Didit derives the party's `country_code` from `address.country` when `address` is an object. When `address` is a string, `country_code` comes back `null` on that party.

When `vendor_data` matches an existing user or business, Didit automatically links the transaction to that entity's profile, enabling cross-session rule evaluation and velocity checks.
In the response, both parties come back in the `parties` array with roles `APPLICANT` and `COUNTERPARTY`.

## Payment methods

Attach a `payment_method` to each party to describe the instrument on that side:

| Field | Type | Description |
| - | - | - |
| `method_type` | string | `bank_card`, `bank_account`, `ewallet`, `crypto_wallet`, `unhosted_wallet`, or `other` |
| `account_id` | string | Account number, wallet address, or card token |
| `issuing_country` | string | Country that issued the payment instrument |

On `travelRule` transactions, a counterparty payment method with `payment_method_type: "unhosted_wallet"` declares the destination as a self-hosted wallet and routes the transfer straight to the ownership-proof flow - see [Declared self-hosted wallets](/transaction-monitoring/travel-rule#declared-self-hosted-wallets).

For crypto transactions, include wallet addresses in `account_id` and set `currency_kind` to `crypto` so blockchain screening can run automatically. Direction controls which wallet is screened: inbound pre-transfer screening uses the sender wallet in `counterparty.payment_method.account_id`; outbound screening uses the destination wallet in `counterparty.payment_method.account_id`. If you send a blockchain hash in `transaction_details.payment_reference_id`, inbound transaction screening requires your service deposit address in `subject.payment_method.account_id`, while outbound transaction screening requires the destination wallet in `counterparty.payment_method.account_id`.

## Crypto screening override

Use `include_crypto_screening` at the top level of the request to override the default configured in **Transactions** > **Settings** in the Console:

| Value | Description |
| - | - |
| `true` | Force crypto screening for this transaction |
| `false` | Skip crypto screening for this transaction |
| omitted or `null` | Use the application-level default |

## Device context

Include device information on the subject (and optionally the counterparty) party to power device intelligence rules. The request field is `device_context` on each party; the camelCase alias `device` is also accepted and is what the SDK wire format uses. The response returns it as `device` on each party in the `parties` array.

| Field | Type | Description |
| - | - | - |
| `device_context.fingerprint` | string | Unique device fingerprint |
| `device_context.persistent_device_id` | string | Stable cross-session device identifier (v2 fingerprint schema) |
| `device_context.user_agent` | string | Browser or app user agent string |
| `device_context.browser_family` | string | Browser family (e.g. `Chrome`, `Firefox`, `Safari`). **Must be sent explicitly** — it is not derived from `user_agent`. Powers rules like `subject_browser_family`. |
| `device_context.browser_version` | string | Browser version string (e.g. `131.0`). Must be sent explicitly. |
| `device_context.platform` | string | Platform identifier (`web`, `ios`, `android`, `macos`, `windows`, `linux`). **Must be sent explicitly** — it is not derived from `user_agent`. Powers rules like `subject_platform`. |
| `device_context.os_family` | string | Operating system family (e.g. `iOS`, `Android`, `Windows`) |
| `device_context.os_version` | string | Operating system version string |
| `device_context.accept_language` | string | Accept-Language header value (e.g. `en-US,en;q=0.9`). On the SDK path this is collected automatically. |
| `device_context.session_id` | string | Session identifier of the user's current session. Stored in metadata. CamelCase alias: `sessionId`. |
| `device_context.session_age_ms` | number | Age of the user's session in milliseconds at transaction time. Powers rules like `subject_session_age_ms`. |
| `device_context.network_context.ip_address` | string | Client IP address |
| `device_context.network_context.ip_country_code` | string | Geo-resolved country of the IP (ISO 3166-1 alpha-3) |
| `device_context.network_context.latitude` | number | Geographic latitude of the IP |
| `device_context.network_context.longitude` | number | Geographic longitude of the IP |

<Note>
  `browser_family` and `platform` are **not** derived from `user_agent`. They must be sent as explicit top-level keys inside `device_context` for device intelligence rules to fire. If you rely on the Didit SDK, these fields are collected automatically.
</Note>

CamelCase aliases are accepted for every field: `userAgent`, `browserFamily`, `browserVersion`, `osFamily`, `osVersion`, `acceptLang`, `sessionId`, `sessionAgeMs`. The `network_context` sub-object accepts `ip` / `countryCode3` / `lat` / `lon` aliases, and a top-level `ipInfo` block is also folded into `network_context`.

Device context enables rules like multi-accounting detection (same fingerprint across different subjects), new-browser-and-new-IP combination detection, low session age high-value transfer, and location-based risk scoring.

## Custom properties

Add any additional key-value pairs in the `custom_properties` object. Rules can reference these fields in their conditions — useful for passing business-specific context like product category, merchant ID, or channel.

```json theme={null}
"custom_properties": {
  "product_category": "electronics",
  "merchant_id": "MER456",
  "channel": "mobile_app"
}
```

In rule conditions, reference each key with the field path `custom_values.<key>` — for example, the payload above matches `custom_values.product_category eq "electronics"`. In the Console rule builder, keys seen on recently ingested transactions appear in the condition field selector under **Custom Properties**, and **Enter custom property** in the same group lets you type any key manually (case-sensitive). See [Rules & scoring](/transaction-monitoring/rules#referencing-custom-properties) for details.

## Example request

```bash theme={null}
curl -X POST https://verification.didit.me/v3/transactions/ \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "transaction_id": "TXN-20260410-001",
    "transaction_at": "2026-04-10T15:30:00Z",
    "transaction_category": "finance",
    "transaction_details": {
      "direction": "outbound",
      "amount": "1500.50",
      "currency": "EUR",
      "currency_kind": "fiat",
      "action_type": "withdrawal"
    },
    "subject": {
      "entity_type": "person",
      "vendor_data": "user-123",
      "full_name": "John Doe",
      "country_code": "US",
      "payment_method": {
        "method_type": "bank_account",
        "account_id": "DE89370400440532013000",
        "issuing_country": "DE"
      }
    },
    "counterparty": {
      "entity_type": "company",
      "full_name": "John Doe",
      "country_code": "GB"
    },
    "custom_properties": {
      "order_id": "ORD-7891"
    }
  }'
```

## Response

The API returns the created transaction with its initial evaluation results:

| Field | Type | Description |
| - | - | - |
| `uuid` | string | Didit's unique transaction identifier |
| `txn_id` | string | Your provided transaction ID |
| `status` | string | `APPROVED`, `IN_REVIEW`, `DECLINED`, or `AWAITING_USER` |
| `score` | number | Cumulative risk score from all matched rules |
| `severity` | string | Highest severity of matched rules (`LOW`, `MEDIUM`, `HIGH`, `CRITICAL`) |
| `decision_reason_code` | string | Machine-readable reason for the status decision |
| `decision_reason_label` | string | Human-readable description of the decision |

See the [API Reference](/management-api/transactions/create) for the full request and response schema, and the dedicated [response schema reference](/transaction-monitoring/response-schema) for an annotated payload walkthrough.

## Idempotency

`txn_id` is unique per application. Re-submitting the same `txn_id` returns the existing transaction rather than creating a duplicate — effectively idempotent for retries. Always generate `txn_id` on your side (e.g. your own transaction DB ID, UUID, or deterministic hash).

## Historical backfill

Pass `txn_date` in the past to ingest historical transactions. Rules still evaluate by default — use the console CSV import with dry-ingest mode if you want to skip rule evaluation during bulk historical loads. See [integration guide](/transaction-monitoring/integration-guide#historical-ingestion).

## Next steps

<CardGroup cols={3}>
  <Card title="Rules & scoring" icon="scale-balanced" href="/transaction-monitoring/rules">
    How submitted transactions are evaluated against rules.
  </Card>

  <Card title="Response schema" icon="file-lines" href="/transaction-monitoring/response-schema">
    Full annotated response payload.
  </Card>

  <Card title="Crypto screening" icon="link" href="/transaction-monitoring/aml-screening">
    Automatic wallet and transaction screening for crypto transactions.
  </Card>

  <Card title="Travel rule" icon="globe" href="/transaction-monitoring/travel-rule">
    Attach travel rule compliance data to virtual asset transfers.
  </Card>

  <Card title="IP enrichment" icon="globe-pointer" href="/transaction-monitoring/ip-enrichment">
    IP-derived context available to rules.
  </Card>

  <Card title="API reference" icon="code" href="/management-api/transactions/create">
    Full request schema for the Create Transaction endpoint.
  </Card>
</CardGroup>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.