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

# Creating velocity rules

> Full field reference for creating a velocity rule: purchase and withdrawal amount/count limits, currency, MCC and region lists, ruleRef idempotency, and every error code.

## 📄 Guide

A velocity rule is a standalone, company-owned object assembled from five control groups: `purchaseControl` (purchases), `cashAdvanceControl` (cash withdrawals), `currencyControl` (currency list), `mccControl` (merchant-category list), and `geographicControl` (region list). A newly created rule is `ACTIVE`, yet constrains nothing until it is bound to a card or an employee — binding is covered in [Binding and unbinding](./binding-and-unbinding), and the overall model in [Setting spend limits](./spend-limits).

## Create a rule

**`POST /open-api-corp/velocity/v1/create-rule`**

<Warning>
  Configure **at least one** of the five control groups — sending none returns `DAPI_PARAM_INVALID`. **"Unlimited" is expressed by omitting the field; never send 0 or a negative number** — doing so returns `VELOCITY_LIMIT_VALUE_INVALID`.
</Warning>

<Warning>
  Two different axes — do not conflate them: amount limits match on the **settlement currency**, whereas the `currencyControl` list judges the **original purchase currency**.
</Warning>

### Request parameters: top level

| Field            | Type   | Required | Description                                                                                                |
| ---------------- | ------ | -------- | ---------------------------------------------------------------------------------------------------------- |
| `ruleRef`        | String | Yes      | ≤64; client-generated idempotency key; retrying with the same company + `ruleRef` returns the first result |
| `organizationId` | String | Yes      | ≤20; the company the rule belongs to                                                                       |
| `ruleName`       | String | Yes      | ≤64; rule name                                                                                             |
| `remark`         | String | No       | ≤256; rule remark                                                                                          |

<Note>
  `ruleRef` is an idempotency key: after a cardNetwork timeout, retry with the same `ruleRef` and DCS will not create a duplicate rule — it returns the result of the first creation unchanged.
</Note>

### Request parameters: purchaseControl

Omit `purchaseControl` to leave purchases unlimited.

| Field                                           | Type    | Required | Description                                                            |
| ----------------------------------------------- | ------- | -------- | ---------------------------------------------------------------------- |
| `purchaseControl.amountLimits`                  | Array   | No       | Purchase amount limits, one entry per currency                         |
| `purchaseControl.amountLimits[].currency`       | String  | Yes      | 3; three-letter currency code (`^[A-Z]{3}$`), e.g. USD                 |
| `purchaseControl.amountLimits[].perTransaction` | String  | No       | Per-transaction limit, > 0                                             |
| `purchaseControl.amountLimits[].daily`          | String  | No       | Daily cumulative limit                                                 |
| `purchaseControl.amountLimits[].monthly`        | String  | No       | Monthly cumulative limit                                               |
| `purchaseControl.amountLimits[].quarterly`      | String  | No       | Quarterly cumulative limit                                             |
| `purchaseControl.amountLimits[].yearly`         | String  | No       | Yearly cumulative limit                                                |
| `purchaseControl.countLimits`                   | Object  | No       | Purchase count limits, not per currency — summed across all currencies |
| `purchaseControl.countLimits.daily`             | Integer | No       | Daily cumulative count, > 0                                            |
| `purchaseControl.countLimits.monthly`           | Integer | No       | Monthly cumulative count                                               |
| `purchaseControl.countLimits.quarterly`         | Integer | No       | Quarterly cumulative count                                             |
| `purchaseControl.countLimits.yearly`            | Integer | No       | Yearly cumulative count                                                |

### Request parameters: cashAdvanceControl

Omit `cashAdvanceControl` to leave withdrawals unlimited. The one difference from the purchase group is the group-level switch `allowed`: whenever this group is configured, `allowed` is required, and `false` bans withdrawals outright.

| Field                                              | Type    | Required | Description                                                                       |
| -------------------------------------------------- | ------- | -------- | --------------------------------------------------------------------------------- |
| `cashAdvanceControl.allowed`                       | Boolean | Yes      | Group-level switch (required within the group); `false` bans withdrawals outright |
| `cashAdvanceControl.amountLimits`                  | Array   | No       | Withdrawal amount limits, one entry per currency                                  |
| `cashAdvanceControl.amountLimits[].currency`       | String  | Yes      | 3; three-letter currency code (`^[A-Z]{3}$`)                                      |
| `cashAdvanceControl.amountLimits[].perTransaction` | String  | No       | Per-withdrawal limit, > 0                                                         |
| `cashAdvanceControl.amountLimits[].daily`          | String  | No       | Daily cumulative withdrawal limit                                                 |
| `cashAdvanceControl.amountLimits[].monthly`        | String  | No       | Monthly cumulative withdrawal limit                                               |
| `cashAdvanceControl.amountLimits[].quarterly`      | String  | No       | Quarterly cumulative withdrawal limit                                             |
| `cashAdvanceControl.amountLimits[].yearly`         | String  | No       | Yearly cumulative withdrawal limit                                                |
| `cashAdvanceControl.countLimits`                   | Object  | No       | Withdrawal count limits, summed across all currencies                             |
| `cashAdvanceControl.countLimits.daily`             | Integer | No       | Daily cumulative withdrawal count, > 0                                            |
| `cashAdvanceControl.countLimits.monthly`           | Integer | No       | Monthly cumulative withdrawal count                                               |
| `cashAdvanceControl.countLimits.quarterly`         | Integer | No       | Quarterly cumulative withdrawal count                                             |
| `cashAdvanceControl.countLimits.yearly`            | Integer | No       | Yearly cumulative withdrawal count                                                |

### Request parameters: the three list controls

All three lists share the same shape: a `type` (`WHITELIST` / `BLACKLIST`) plus a non-empty detail list.

| Field                            | Type   | Required | Description                                                               |
| -------------------------------- | ------ | -------- | ------------------------------------------------------------------------- |
| `currencyControl`                | Object | No       | Purchase-currency list, judged against the **original purchase currency** |
| `currencyControl.type`           | Enum   | Yes      | `WHITELIST` / `BLACKLIST`                                                 |
| `currencyControl.currencies`     | Array  | Yes      | 3 per item; non-empty; three-letter currency codes                        |
| `mccControl`                     | Object | No       | Merchant-category list                                                    |
| `mccControl.type`                | Enum   | Yes      | `WHITELIST` / `BLACKLIST`                                                 |
| `mccControl.mccList`             | Array  | Yes      | 4 per item; non-empty; 4-digit MCCs                                       |
| `geographicControl`              | Object | No       | Transaction-region list, judged against the merchant country code         |
| `geographicControl.type`         | Enum   | Yes      | `WHITELIST` / `BLACKLIST`                                                 |
| `geographicControl.countryCodes` | Array  | Yes      | 2 per item; non-empty; two-letter ISO country codes (uppercase)           |

### Response data

The response `data` is the complete rule object: it echoes all five control groups (an unconfigured group comes back as `null`, and so does any unconfigured field within a group), plus the following platform-managed fields:

| Field            | Type    | Description                                                                             |
| ---------------- | ------- | --------------------------------------------------------------------------------------- |
| `ruleId`         | String  | Rule ID (≤32), platform-generated and globally unique; all subsequent operations use it |
| `organizationId` | String  | Owning company                                                                          |
| `status`         | Enum    | `ACTIVE` / `INACTIVE`; always `ACTIVE` on creation                                      |
| `ruleVersion`    | Integer | Optimistic-lock version, echoed back on update / status change; 1 on creation           |
| `bindingCount`   | Long    | Current number of bound objects; 0 on creation                                          |
| `createTime`     | String  | Creation time, ISO-8601                                                                 |
| `modifyTime`     | String  | Update time, ISO-8601; equals `createTime` on creation                                  |

### Request example

```json theme={null}
{
  "ruleRef": "ext-request-0001",
  "organizationId": "e7c9a1f0-3b52-4d8e-9a67-1f2b3c4d5e6f",
  "ruleName": "Employee travel",
  "remark": "Purchase and cash-advance limits for travel cards",
  "purchaseControl": {
    "amountLimits": [
      { "currency": "USD", "perTransaction": "3000.00", "daily": "10000.00",
        "monthly": "80000.00", "quarterly": "200000.00", "yearly": "600000.00" },
      { "currency": "HKD", "daily": "50000.00" }
    ],
    "countLimits": { "daily": 5, "monthly": 100 }
  },
  "cashAdvanceControl": {
    "allowed": true,
    "amountLimits": [
      { "currency": "USD", "perTransaction": "500.00", "daily": "2000.00", "monthly": "10000.00" }
    ]
  }
}
```

### Response example

```json theme={null}
{
  "code": "SYS_SUCCESS",
  "message": null,
  "data": {
    "ruleId": "R000456",
    "organizationId": "e7c9a1f0-3b52-4d8e-9a67-1f2b3c4d5e6f",
    "ruleName": "Employee travel",
    "remark": "Purchase and cash-advance limits for travel cards",
    "status": "ACTIVE",
    "ruleVersion": 1,
    "bindingCount": 0,
    "createTime": "2026-08-12T10:00:00Z",
    "modifyTime": "2026-08-12T10:00:00Z",
    "purchaseControl": {
      "amountLimits": [
        { "currency": "USD", "perTransaction": "3000.00", "daily": "10000.00",
          "monthly": "80000.00", "quarterly": "200000.00", "yearly": "600000.00" },
        { "currency": "HKD", "perTransaction": null, "daily": "50000.00",
          "monthly": null, "quarterly": null, "yearly": null }
      ],
      "countLimits": { "daily": 5, "monthly": 100, "quarterly": null, "yearly": null }
    },
    "cashAdvanceControl": {
      "allowed": true,
      "amountLimits": [
        { "currency": "USD", "perTransaction": "500.00", "daily": "2000.00",
          "monthly": "10000.00", "quarterly": null, "yearly": null }
      ],
      "countLimits": null
    },
    "currencyControl": null,
    "mccControl": null,
    "geographicControl": null
  }
}
```

### Error codes

| Error code                      | Description                                                                                                                                                   |
| ------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `ORGANIZATION_INVALID`          | Company missing, not ACTIVE, or not owned by this partner                                                                                                     |
| `DAPI_PARAM_INVALID`            | None of the 5 control groups is configured (structural gaps use the general parameter error)                                                                  |
| `VELOCITY_LIMIT_VALUE_INVALID`  | Invalid limit value: negative, malformed, over precision, or a longer period smaller than a shorter one ("unlimited" means omitting the field, not sending 0) |
| `VELOCITY_CURRENCY_INVALID`     | Currency in an amount-limit group invalid or duplicated                                                                                                       |
| `VELOCITY_LIST_CONTROL_INVALID` | List control group invalid: bad `type`, empty list, or malformed entries (MCC not 4 digits / country code not two uppercase letters)                          |

The full code set is in the [Error code dictionary](../customer-success/error-codes).

## Next steps

* Bind the rule to cards or employees so it takes effect: [Binding and unbinding](./binding-and-unbinding)
* Query merged effective quotas, update or toggle rules: [Quota query and adjustment](./quota-and-adjustment)
