# Rules

> Create, list, get, update, turn off and delete call forwarding rules, and upload or remove hold audio, with the Edesy Call Forwarding API.

Source: https://edesy.in/docs/call-forwarding/api/rules

A forwarding rule is the forwarding setup for one of your numbers. Create it with `POST /v1/forwarding/rules` and forwarding switches on within a few seconds; change it with `PATCH`, turn it off with `POST /disable` and remove it with `DELETE`. Each number has one rule, and creating it again for the same number returns the existing rule.

## The rule object

```json
{
  "id": "3f2b8c1e-7d4a-4e59-9b21-6a0c5d8e1f47",
  "tenant_id": "5e7a2d19-0b4c-4f83-a6e1-8d2c9f3b7a04",
  "did_number": "8071387146",
  "name": "Main line",
  "strategy": "simultaneous",
  "ring_timeout_sec": 25,
  "confirm_required": true,
  "caller_id_mode": "did",
  "targets": [
    { "number": "9876543210", "priority": 1, "enabled": true, "label": "Sales" },
    { "number": "9123456789", "priority": 2, "enabled": true, "label": "Support" }
  ],
  "status": "active",
  "answer_on_bridge": true,
  "hold_audio_enabled": false,
  "hold_audio_filename": "",
  "sticky_enabled": false,
  "sticky_window_hours": 0,
  "created_at": "2026-10-16T09:30:00Z",
  "updated_at": "2026-10-16T09:30:00Z"
}
```

## Rule settings

Used by create (all optional except `did_number` and `targets`) and update (all optional).

| Field | Type | Description |
|---|---|---|
| `name` | string | Your label, up to 255 characters |
| `strategy` | `simultaneous` or `sequential` | Ring all at once (default) or one by one in list order. See [Ring Strategies](https://edesy.in/docs/call-forwarding/rules/ring-strategies) |
| `ring_timeout_sec` | integer, 5 to 120 | How long to ring, in seconds. Default 25. With `sequential`, per number unless the target sets its own `ring_timeout` |
| `confirm_required` | boolean | Default `true`. "Press any key to accept" for the person who answers. See [Call Screening](https://edesy.in/docs/call-forwarding/rules/call-screening) |
| `answer_on_bridge` | boolean | Default `true`. The caller hears real ringing and billing starts only when someone answers. Ignored while hold audio is on |
| `caller_id_mode` | `did` or `original` | Your virtual number (default) or the real caller's number. See [Caller ID Display](https://edesy.in/docs/call-forwarding/rules/caller-id-display) |
| `sticky_enabled` | boolean | Default `false`. Ring the person who answered this caller last time first (ring one by one). See [Sticky Agent](https://edesy.in/docs/call-forwarding/rules/sticky-agent) |
| `sticky_window_hours` | integer, 1 to 8,760 | How long the caller is remembered. 168 (7 days) when sticky is on and no window is set |
| `targets` | array, up to 5 | The phones to ring, in ring order (see below) |

Each target:

| Field | Type | Description |
|---|---|---|
| `number` | string, required | Indian mobile or landline, any format (`+91...`, `91...`, `0...` or 10 digits) |
| `label` | string | Up to 64 characters, e.g. "Sales desk" |
| `enabled` | boolean | Default `true`. A disabled target stays on the rule but does not ring |
| `ring_timeout` | integer, 5 to 120 | Seconds this target rings, `sequential` only |

`priority` is set for you from the list order.

## Create a rule

```
POST /v1/forwarding/rules
```

Creates a rule for one of your numbers and switches forwarding on.

| Field | Type | Description |
|---|---|---|
| `did_number` | string, required | Your number that people call. `8071387146`, `+918071387146`, `918071387146` or `08071387146`; stored as 10 digits |
| `targets` | array, required | At least one number to ring |
| other settings | | Any field from [Rule settings](#rule-settings) |

```bash
curl -X POST https://voice-api.edesy.in/v1/forwarding/rules \
  -H "Authorization: Bearer sk_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "did_number": "+918071387146",
    "name": "Main line",
    "targets": [
      { "number": "9876543210", "label": "Sales" },
      { "number": "9123456789", "label": "Support" }
    ]
  }'
```

Returns `201` with `{ "data": <rule>, "meta": {...} }`.

- The number must be in the same workspace as your API key. Buy it first if it is not.
- Sending the request again for a number that already has your rule is safe: you get the same rule back and nothing is duplicated.

| Status | Code | When |
|---|---|---|
| 201 | | Forwarding is on |
| 400 | `validation_error` | A field is wrong; the message names it |
| 400 | `targets_required` | No number to forward to |
| 404 | `number_not_found` | The number is not in your workspace |
| 409 | `rule_exists` | Another account still holds forwarding on this number |
| 502 | `inbound_activation_failed` | Saved but not switched on yet. Send the same request again |

## List rules

```
GET /v1/forwarding/rules
```

Every rule in your workspace, active and paused, in `data`. Not paginated.

## Get a rule

```
GET /v1/forwarding/rules/:id
```

One rule by its `id`. `404 not_found` if it is not in your workspace.

## Update a rule

```
PATCH /v1/forwarding/rules/:id
```

Send only the fields you want to change. Accepts every field in [Rule settings](#rule-settings), plus:

| Field | Type | Description |
|---|---|---|
| `status` | `active` or `paused` | Switch the rule on, or pause it. A paused number tells callers "This number is not in service"; to hand the number back to its normal routing, use [Turn off forwarding](#turn-off-forwarding) instead |
| `hold_audio_enabled` | boolean | Play audio while the phones ring: your clip, or the built-in "please hold" message |

`targets` replaces the whole list. To add one person, send the existing targets plus the new one.

```bash
curl -X PATCH https://voice-api.edesy.in/v1/forwarding/rules/RULE_ID \
  -H "Authorization: Bearer sk_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "strategy": "sequential",
    "targets": [
      { "number": "9876543210", "label": "Sales" },
      { "number": "9123456789", "label": "Support" },
      { "number": "9000012345", "label": "Manager" }
    ]
  }'
```

Returns `200` with the updated rule.

## Turn off forwarding

```
POST /v1/forwarding/rules/:id/disable
```

Stops forwarding and hands the number back to whatever it did before (for example your voice agent or app). The rule is kept with `status: "paused"`; switch it back on with `PATCH` `{"status": "active"}`. Safe to repeat. A `502 inbound_repoint_failed` means forwarding is already off; send the request again to finish. See [Turn Off a Rule](https://edesy.in/docs/call-forwarding/rules/disable-a-rule).

## Delete a rule

```
DELETE /v1/forwarding/rules/:id
```

Removes the rule and hands the number back to its normal routing. Call logs are kept. Returns `204` with no body, also when the rule is already gone.

## Hold audio

```
POST /v1/forwarding/rules/:id/hold-audio
DELETE /v1/forwarding/rules/:id/hold-audio
```

Upload a message or music the caller hears while your team's phones ring: MP3, WAV, M4A or OGG, up to 8 MB. Short clips loop. Send `multipart/form-data` with the file in a field named `file`, not JSON.

```bash
curl -X POST https://voice-api.edesy.in/v1/forwarding/rules/RULE_ID/hold-audio \
  -H "Authorization: Bearer sk_your_api_key" \
  -F "file=@please-hold.mp3"
```

- Uploading turns hold audio on for the rule. While it is on, the caller is answered straight away to hear the clip, so billing starts then.
- `DELETE` removes your clip. If hold audio is still on, callers hear the built-in "please hold" message. To stop hold audio, `PATCH` `{"hold_audio_enabled": false}`.

| Status | Code | When |
|---|---|---|
| 200 | | Uploaded (or removed); returns the rule |
| 400 | `invalid_request` | No `file` field, or not readable audio |
| 413 | `file_too_large` | Over 8 MB |
| 404 | `not_found` | No such rule |

See [Hold Music](https://edesy.in/docs/call-forwarding/rules/hold-music).
