> ## 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.

# Create Rule

> Create a CUSTOM transaction-monitoring rule. Titles are unique per application (duplicate -> 400). A `change_status` action targeting `AWAITING_USER` must carry `workflow_id`. Rules created in ACTIVE mode apply to new transactions immediately - the recommended workflow is create in TEST mode, backtest, then switch mode to ACTIVE.

export const AgentPromptAccordion = ({prompt, title = "AI Agent Integration Prompt"}) => {
  const [copied, setCopied] = React.useState(false);
  const handleCopy = e => {
    e.stopPropagation();
    if (!prompt) return;
    navigator.clipboard.writeText(prompt.trim()).then(() => {
      setCopied(true);
      setTimeout(() => setCopied(false), 2000);
    });
  };
  const agents = ["Claude Code", "Codex", "Cursor", "Devin", "Windsurf", "GitHub Copilot"];
  return <div className="didit-agent-card">
      {}
      <div className="didit-agent-titlebar">
        <div className="didit-agent-dots" aria-hidden="true">
          <span className="didit-agent-dot didit-agent-dot-red"></span>
          <span className="didit-agent-dot didit-agent-dot-yellow"></span>
          <span className="didit-agent-dot didit-agent-dot-green"></span>
        </div>
        <span className="didit-agent-filename">{title}</span>
        <button type="button" className={`didit-agent-copy ${copied ? "didit-agent-copy-copied" : ""}`} onClick={handleCopy} title="Copy prompt to clipboard" aria-label={copied ? "Copied!" : "Copy prompt to clipboard"}>
          {copied ? <>
              <svg width="13" height="13" viewBox="0 0 16 16" fill="none">
                <path d="M3 8.5l3.5 3.5L13 4" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
              </svg>
              <span>Copied</span>
            </> : <>
              <svg width="13" height="13" viewBox="0 0 16 16" fill="none">
                <rect x="5" y="5" width="9" height="9" rx="1.5" stroke="currentColor" strokeWidth="1.5" />
                <path d="M11 5V3.5A1.5 1.5 0 0 0 9.5 2h-6A1.5 1.5 0 0 0 2 3.5v6A1.5 1.5 0 0 0 3.5 11H5" stroke="currentColor" strokeWidth="1.5" />
              </svg>
              <span>Copy</span>
            </>}
        </button>
      </div>

      {}
      <pre className="didit-agent-body"><code>{prompt.trim()}</code></pre>

      {}
      <div className="didit-agent-footer">
        <span className="didit-agent-footer-label">Paste into</span>
        <div className="didit-agent-chips">
          {agents.map(name => <span key={name} className="didit-agent-chip">{name}</span>)}
        </div>
      </div>
    </div>;
};

<AgentPromptAccordion
  title="Create Rule API Prompt"
  prompt={`Create a custom transaction-monitoring rule on my Didit application.

Endpoint:
POST https://verification.didit.me/v3/transactions/rules/

Authentication:
Use the x-api-key header with my Didit API key.

Request body (application/json):
{
"title": "Structuring - inbound (custom)",   // required, unique per application - duplicate -> 400
"description": "20+ inbound transfers under 10,000 in 30 days.",
"category": "finance",                        // required: finance, aml_ctf, anomaly_detection, fatf, device_intelligence, crypto_monitoring, travel_rule, responsible_gaming, e_commerce, ...
"mode": "TEST",                               // required: TEST (recommended start - evaluates, never affects transactions), ACTIVE, DISABLED
"evaluation_mode": "ALL",                     // ALL = every condition must match, ANY = at least one
"scope": {"transaction_types": ["finance"]},  // optional keys: transaction_types, directions (INBOUND/OUTBOUND), action_types
"conditions": [
{"field": "amount", "operator": "lt", "value": 10000}
],
"aggregation": [                              // velocity: ALL entries must match
{"metric": "count", "operator": "gte", "value": 20, "window": "30d",
 "filters": {"direction": "INBOUND", "subject_vendor_data": "__current__"}}
],
"actions": [{"type": "add_score", "value": 35}]
}

Schema essentials:
- Condition operators: eq, ne, gt, gte, lt, lte, in, not_in, contains, not_contains, contains_any (list of strings), regex, fuzzy_match (needs "score" 0-100), exists/is_not_empty/is_not_null, not_exists/is_empty/is_null.
- Condition fields include amount, preferred_currency_amount, direction, action_type, payment_details, subject_vendor_data, counterparty_country, subject_device_fingerprint, subject_payment_method_*, travel_rule_status, tags, and custom_values.<key> for your own custom_properties.
- value_type turns "value" into a List UUID ("list"), another field path ("field"), or a relative date ("relative_date").
- Aggregation windows: <N>m (MINUTES), <N>h, <N>d. A filter value of "__current__" means same-as-the-evaluated-transaction.
- Actions: add_score {value}, change_status {value, workflow_id (REQUIRED for AWAITING_USER)}, add_tags {tag_uuid|tag_name, tag_color}, add_note {note}, add_to_list {list_id}, open_case {blueprint, grouping, attach_matched_transaction}.

Recommended workflow:
1. Create in mode TEST.
2. POST /v3/transactions/rules/backtest/ with the same conditions/aggregation to see matches over your history.
3. PATCH the rule to mode ACTIVE once the numbers look right.

Failure modes:
- 400 - duplicate title, unknown operator/metric, malformed window, invalid scope direction, contains_any without a list, fuzzy_match without a score, or AWAITING_USER without workflow_id. The body names the exact index and problem.
- 401 - missing or malformed x-api-key.

Full guide: /transaction-monitoring/rules-api.`}
/>


## OpenAPI

````yaml POST /v3/transactions/rules/
openapi: 3.0.0
info:
  version: 3.0.0
  title: Didit Verification API
  description: Identity verification API. Authenticate with x-api-key header.
servers:
  - url: https://verification.didit.me
security: []
tags: []
paths:
  /v3/transactions/rules/:
    post:
      tags:
        - Transactions
      summary: Create rule
      description: >-
        Create a CUSTOM transaction-monitoring rule. Titles are unique per
        application (duplicate -> 400). A `change_status` action targeting
        `AWAITING_USER` must carry `workflow_id`. Rules created in ACTIVE mode
        apply to new transactions immediately - the recommended workflow is
        create in TEST mode, backtest, then switch mode to ACTIVE.
      operationId: createTransactionRule
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/TransactionRuleCreateRequest'
            example:
              title: Structuring - inbound (custom)
              description: >-
                20+ inbound transfers under 10,000 in 30 days from the same
                subject.
              category: finance
              mode: TEST
              evaluation_mode: ALL
              scope:
                transaction_types:
                  - finance
              conditions:
                - field: amount
                  operator: lt
                  value: 10000
              aggregation:
                - metric: count
                  operator: gte
                  value: 20
                  window: 30d
                  filters:
                    direction: INBOUND
                    subject_vendor_data: __current__
              actions:
                - type: add_score
                  value: 35
      responses:
        '201':
          description: The created rule.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TransactionRuleDetail'
        '400':
          description: >-
            Validation error - duplicate title, unknown operator/metric,
            malformed window, invalid scope direction, or AWAITING_USER without
            workflow_id.
          content:
            application/json:
              schema:
                type: object
              example:
                title:
                  - A rule with this name already exists.
      security:
        - ApiKeyAuth: []
      x-codeSamples:
        - lang: curl
          label: curl
          source: >-
            curl -X POST 'https://verification.didit.me/v3/transactions/rules/'
            \
              -H 'x-api-key: YOUR_API_KEY' \
              -H 'Content-Type: application/json' \
              -d '{"title": "Structuring - inbound (custom)", "description": "20+ inbound transfers under 10,000 in 30 days from the same subject.", "category": "finance", "mode": "TEST", "evaluation_mode": "ALL", "scope": {"transaction_types": ["finance"]}, "conditions": [{"field": "amount", "operator": "lt", "value": 10000}], "aggregation": [{"metric": "count", "operator": "gte", "value": 20, "window": "30d", "filters": {"direction": "INBOUND", "subject_vendor_data": "__current__"}}], "actions": [{"type": "add_score", "value": 35}]}'
        - lang: python
          label: Python
          source: |-
            import os

            import requests

            rule = {
              "title": "Structuring - inbound (custom)",
              "description": "20+ inbound transfers under 10,000 in 30 days from the same subject.",
              "category": "finance",
              "mode": "TEST",
              "evaluation_mode": "ALL",
              "scope": {
                "transaction_types": [
                  "finance"
                ]
              },
              "conditions": [
                {
                  "field": "amount",
                  "operator": "lt",
                  "value": 10000
                }
              ],
              "aggregation": [
                {
                  "metric": "count",
                  "operator": "gte",
                  "value": 20,
                  "window": "30d",
                  "filters": {
                    "direction": "INBOUND",
                    "subject_vendor_data": "__current__"
                  }
                }
              ],
              "actions": [
                {
                  "type": "add_score",
                  "value": 35
                }
              ]
            }

            resp = requests.post(
                'https://verification.didit.me/v3/transactions/rules/',
                headers={'x-api-key': os.environ['DIDIT_API_KEY']},
                json=rule,
                timeout=15,
            )
            resp.raise_for_status()
            print(resp.json()['uuid'], resp.json()['mode'])
        - lang: javascript
          label: JavaScript
          source: >-
            const rule = {
              "title": "Structuring - inbound (custom)",
              "description": "20+ inbound transfers under 10,000 in 30 days from the same subject.",
              "category": "finance",
              "mode": "TEST",
              "evaluation_mode": "ALL",
              "scope": {
                "transaction_types": [
                  "finance"
                ]
              },
              "conditions": [
                {
                  "field": "amount",
                  "operator": "lt",
                  "value": 10000
                }
              ],
              "aggregation": [
                {
                  "metric": "count",
                  "operator": "gte",
                  "value": 20,
                  "window": "30d",
                  "filters": {
                    "direction": "INBOUND",
                    "subject_vendor_data": "__current__"
                  }
                }
              ],
              "actions": [
                {
                  "type": "add_score",
                  "value": 35
                }
              ]
            };


            const resp = await
            fetch('https://verification.didit.me/v3/transactions/rules/', {
              method: 'POST',
              headers: {
                'x-api-key': process.env.DIDIT_API_KEY,
                'Content-Type': 'application/json',
              },
              body: JSON.stringify(rule),
            });

            if (!resp.ok) throw new Error(await resp.text());

            const created = await resp.json();

            console.log(created.uuid, created.mode);
components:
  schemas:
    TransactionRuleCreateRequest:
      type: object
      required:
        - title
        - category
        - mode
      properties:
        title:
          type: string
          description: >-
            Rule name. Unique per application among non-deleted rules;
            duplicates return 400.
        description:
          type: string
          nullable: true
        category:
          type: string
          description: >-
            Risk theme used for grouping and reporting, e.g. finance, aml_ctf,
            anomaly_detection, fatf, device_intelligence, crypto_monitoring,
            travel_rule, responsible_gaming, e_commerce.
        mode:
          type: string
          enum:
            - ACTIVE
            - DISABLED
            - TEST
          description: >-
            ACTIVE evaluates live. TEST evaluates and records rule runs but
            never touches score or status - the recommended starting mode.
            DISABLED is skipped.
        evaluation_mode:
          type: string
          enum:
            - ALL
            - ANY
          default: ALL
          description: >-
            Whether every condition must match (ALL) or at least one (ANY).
            Ignored when conditions use group_index.
        scope:
          $ref: '#/components/schemas/TransactionRuleScope'
        conditions:
          type: array
          items:
            $ref: '#/components/schemas/TransactionRuleCondition'
        aggregation:
          type: array
          items:
            $ref: '#/components/schemas/TransactionRuleAggregation'
        actions:
          type: array
          items:
            $ref: '#/components/schemas/TransactionRuleAction'
        metadata:
          type: object
          description: Free-form metadata stored on the rule.
      description: >-
        Creates a CUSTOM rule. Rules in ACTIVE mode take effect immediately for
        new transactions - create in TEST mode and backtest first.
    TransactionRuleDetail:
      type: object
      properties:
        uuid:
          type: string
          format: uuid
        title:
          type: string
          description: >-
            Rule name. Unique per application among non-deleted rules;
            duplicates return 400.
        description:
          type: string
          nullable: true
        category:
          type: string
          description: >-
            Risk theme used for grouping and reporting, e.g. finance, aml_ctf,
            anomaly_detection, fatf, device_intelligence, crypto_monitoring,
            travel_rule, responsible_gaming, e_commerce.
        mode:
          type: string
          enum:
            - ACTIVE
            - DISABLED
            - TEST
          description: >-
            ACTIVE evaluates live. TEST evaluates and records rule runs but
            never touches score or status - the recommended starting mode.
            DISABLED is skipped.
        evaluation_mode:
          type: string
          enum:
            - ALL
            - ANY
          default: ALL
          description: >-
            Whether every condition must match (ALL) or at least one (ANY).
            Ignored when conditions use group_index.
        scope:
          $ref: '#/components/schemas/TransactionRuleScope'
        conditions:
          type: array
          items:
            $ref: '#/components/schemas/TransactionRuleCondition'
        aggregation:
          type: array
          items:
            $ref: '#/components/schemas/TransactionRuleAggregation'
        actions:
          type: array
          items:
            $ref: '#/components/schemas/TransactionRuleAction'
        metadata:
          type: object
          description: Free-form metadata stored on the rule.
        severity:
          type: string
          enum:
            - UNKNOWN
            - LOW
            - MEDIUM
            - HIGH
            - CRITICAL
          nullable: true
          description: >-
            Informational severity carried by library rules; not settable on
            custom rules.
        source:
          type: string
          enum:
            - PRESET
            - CUSTOM
          description: >-
            PRESET rules come from the library (install/uninstall, mode-only
            edits); CUSTOM rules are fully yours.
        library_key:
          type: string
          nullable: true
          description: Stable library identifier; null for custom rules.
        latest_triggered_at:
          type: string
          format: date-time
          nullable: true
        run_count:
          type: integer
          description: How many transactions matched this rule.
        approved_count:
          type: integer
        reviewed_count:
          type: integer
        declined_count:
          type: integer
        awaiting_user_count:
          type: integer
        approved_pct:
          type: number
        reviewed_pct:
          type: number
        declined_pct:
          type: number
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
    TransactionRuleScope:
      type: object
      properties:
        transaction_types:
          type: array
          items:
            type: string
          description: >-
            Restrict to transaction types, e.g. finance, kyc, travel_rule,
            user_event. Empty or missing = no restriction.
        directions:
          type: array
          items:
            type: string
            enum:
              - INBOUND
              - OUTBOUND
          description: Restrict to transaction directions (case-insensitive on write).
        action_types:
          type: array
          items:
            type: string
          description: Restrict to your action_type values, e.g. withdrawal, deposit.
      description: >-
        Which transactions the rule applies to. Only these three keys are
        accepted.
    TransactionRuleCondition:
      type: object
      required:
        - field
        - operator
      properties:
        field:
          type: string
          description: >-
            Transaction field path to evaluate. Core fields: amount, currency,
            direction, action_type, score, severity, transaction_type,
            payment_details, preferred_currency_amount (falls back to
            amount_in_default_currency), default_currency_code, subject_country,
            counterparty_country, subject_vendor_data, counterparty_vendor_data,
            subject_device_fingerprint, counterparty_device_fingerprint,
            subject_browser_family, subject_browser_version, subject_platform,
            subject_accept_language, subject_session_age_ms, subject_ip_country,
            counterparty_ip_country, subject_ip_address,
            counterparty_ip_address, subject_payment_method_type,
            subject_payment_method_country, subject_payment_method_fingerprint,
            subject_payment_method_account_id (and the
            counterparty_payment_method_* equivalents), travel_rule_status,
            travel_rule_required, travel_rule_obligations_count,
            subject_days_since_previous_transaction, tags, and
            custom_values.<key> for any key submitted in custom_properties on
            transaction create. Country-code fields accept ISO-2 or ISO-3; both
            sides are normalized to ISO-3 before comparison.
        operator:
          type: string
          enum:
            - eq
            - ne
            - gt
            - gte
            - lt
            - lte
            - in
            - not_in
            - contains
            - not_contains
            - contains_any
            - regex
            - fuzzy_match
            - exists
            - is_not_empty
            - is_not_null
            - not_exists
            - is_empty
            - is_null
          description: >-
            Comparison operator. in/not_in test membership (value may be a list
            or a single scalar). contains/not_contains are case-insensitive
            substring checks against a string value. contains_any takes a list
            of strings and matches when any is a case-insensitive substring of
            the field value. regex matches the value pattern against the field.
            fuzzy_match compares against the value string with the required
            `score` threshold (0-100). gt/gte/lt/lte never match when either
            side is missing. exists/is_not_empty/is_not_null (aliases) and
            not_exists/is_empty/is_null (aliases) are presence checks that
            ignore `value`.
        value:
          description: >-
            Comparison value. Its shape depends on the operator and value_type;
            omit for the presence-check operators.
        value_type:
          type: string
          enum:
            - list
            - field
            - relative_date
          description: >-
            Optional value interpretation. `list`: value is a List UUID and the
            list's entries become the comparison value. `field`: value is
            another field path, compared field-to-field. `relative_date`: value
            is a relative-date object, e.g. {"direction": "past", "unit":
            "days", "amount": 30}.
        score:
          type: number
          minimum: 0
          maximum: 100
          description: >-
            Required when operator is fuzzy_match: the 0-100 similarity
            threshold.
        group_index:
          type: integer
          description: >-
            Optional grouped logic. When any condition carries group_index,
            conditions sharing an index form a group and grouped evaluation
            replaces evaluation_mode.
        group_logic:
          type: string
          enum:
            - AND
            - OR
          default: AND
          description: How conditions combine WITHIN a group.
        groups_logic:
          type: string
          enum:
            - AND
            - OR
          default: OR
          description: >-
            How group outcomes combine ACROSS groups. Read from the first
            condition.
    TransactionRuleAggregation:
      type: object
      required:
        - value
      properties:
        metric:
          type: string
          enum:
            - count
            - sum
            - max
            - min
            - avg
            - distinct_count
          default: count
          description: >-
            How the matching historical transactions are aggregated.
            unique_count is accepted as an alias of distinct_count.
        field:
          type: string
          default: amount
          description: >-
            Field aggregated by sum/max/min/avg/distinct_count. Ignored for
            count.
        operator:
          type: string
          enum:
            - eq
            - ne
            - gt
            - gte
            - lt
            - lte
          default: eq
          description: Comparison applied to the computed metric.
        value:
          type: number
          description: Threshold the computed metric is compared against.
        window:
          type: string
          pattern: ^\d+(m|h|d)$
          default: 1d
          description: >-
            Look-back window ending at the transaction's txn_date: <N>m
            (MINUTES, not months), <N>h (hours) or <N>d (days), e.g. 30m, 24h,
            7d, 30d.
        filters:
          type: object
          additionalProperties: true
          description: >-
            Narrows which historical transactions are aggregated. Keys are field
            paths from the condition field catalog; a value of "__current__"
            resolves to the same field's value on the transaction being
            evaluated (e.g. {"subject_vendor_data": "__current__"} scopes the
            window to the same subject), and a list value means membership.
      description: >-
        Velocity check over the application's historical transactions inside the
        window. Every aggregation entry must match for the rule to match,
        regardless of evaluation_mode.
    TransactionRuleAction:
      oneOf:
        - type: object
          required:
            - type
            - value
          properties:
            type:
              type: string
              enum:
                - add_score
            value:
              type: integer
              description: Points added to the transaction's risk score. May be negative.
          description: >-
            Add points to the transaction's cumulative risk score; the total
            decides the status via the application's review/decline thresholds.
        - type: object
          required:
            - type
            - value
          properties:
            type:
              type: string
              enum:
                - change_status
            value:
              type: string
              enum:
                - APPROVED
                - IN_REVIEW
                - DECLINED
                - AWAITING_USER
              description: >-
                Status to set immediately, regardless of score. Alias key:
                status.
            workflow_id:
              type: string
              format: uuid
              description: >-
                REQUIRED when value is AWAITING_USER: the verification workflow
                the subject is asked to complete. Alias key:
                remediation_workflow_id. Didit creates a hosted remediation
                session for the subject when the rule fires.
          description: >-
            Directly set the transaction status. DECLINED always wins across
            matched rules.
        - type: object
          required:
            - type
          properties:
            type:
              type: string
              enum:
                - add_tags
            tag_uuid:
              type: string
              format: uuid
              description: Existing application tag to attach.
            tag_name:
              type: string
              description: >-
                Tag name to attach; created on first use (stored uppercased).
                Provide tag_uuid or tag_name.
            tag_color:
              type: string
              description: 'Hex color used when tag_name creates a new tag, e.g. #007AFE.'
          description: Attach an application tag to the matched transaction.
        - type: object
          required:
            - type
            - note
          properties:
            type:
              type: string
              enum:
                - add_note
            note:
              type: string
              description: >-
                Note text attached to the transaction (author: Rule Engine).
                Alias key: value.
          description: Attach an automated analyst note to the matched transaction.
        - type: object
          required:
            - type
            - list_id
          properties:
            type:
              type: string
              enum:
                - add_to_list
            list_id:
              type: string
              format: uuid
              description: List to add the subject's vendor_data to.
          description: >-
            Add the subject's vendor_data to a blocklist, allowlist or custom
            list.
        - type: object
          required:
            - type
          properties:
            type:
              type: string
              enum:
                - open_case
            blueprint:
              type: string
              format: uuid
              description: >-
                Case blueprint UUID; defaults to the application's default
                blueprint.
            grouping:
              type: string
              enum:
                - by_applicant
                - by_rule_and_applicant
              description: >-
                Whether repeat matches join one case per applicant or one per
                rule+applicant.
            attach_matched_transaction:
              type: boolean
              description: Attach the matched transaction to the case.
          description: >-
            Open (or add to) an investigation case. Only the first open_case
            action on a rule is applied.
      description: >-
        One rule action. A rule can carry several; all are applied together when
        it matches. Rules in TEST mode record what they would have done without
        applying any action.
  securitySchemes:
    ApiKeyAuth:
      type: apiKey
      in: header
      name: x-api-key

````