# Create a Session

> POST /masking/sessions — bind two real numbers to one virtual number for a limited lifetime and get the virtual number back. Idempotent on your reference id.

Source: https://edesy.in/docs/number-masking/sessions/create-session

```
POST /masking/sessions
```

Binds two real numbers to one virtual number and returns the virtual number. Either party can then call that number and is bridged to the other — routing is decided by who is calling, so the same number works in both directions. **Idempotent on `reference`**: repeating the call for an active reference returns the existing session.

## Body parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `party_a` | string | Yes | First participant — 10-digit Indian mobile (with or without `+91`/`91`; we normalize). |
| `party_b` | string | Yes | Second participant. Either party may call the virtual number. |
| `reference` | string | No | Your external id for this session (order/ride/ticket id). Makes create idempotent and appears on events and call logs. |
| `expiry_minutes` | integer | No | Auto-expire the session after this many minutes (1–43200, i.e. up to 30 days). Omit for no expiry — end it explicitly instead. |
| `max_calls` | integer | No | Optional cap on the number of calls this session will route. |
| `metadata` | object | No | Arbitrary JSON stored with the session and echoed back on reads. |

## Request

```bash
curl -X POST https://voice-api.edesy.in/v1/masking/sessions \
  -H "Authorization: Bearer vp_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "party_a": "9876543210",
    "party_b": "9123456789",
    "reference": "ORD-4821",
    "expiry_minutes": 30
  }'
```

```javascript
const response = await fetch("https://voice-api.edesy.in/v1/masking/sessions", {
  method: "POST",
  headers: {
    "Authorization": "Bearer vp_YOUR_API_KEY",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    party_a: "9876543210",
    party_b: "9123456789",
    reference: "ORD-4821",
    expiry_minutes: 30,
  }),
});
const data = await response.json();
console.log(data);
```

```python
import requests

response = requests.post(
    "https://voice-api.edesy.in/v1/masking/sessions",
    headers={
        "Authorization": "Bearer vp_YOUR_API_KEY",
        "Content-Type": "application/json",
    },
    json={
        "party_a": "9876543210",
        "party_b": "9123456789",
        "reference": "ORD-4821",
        "expiry_minutes": 30,
    },
)
print(response.json())
```

## Responses

**`201` — Session created (or the existing one for this reference)**

```json
{
  "data": {
    "session_id": "3f9a1c72-5b8e-4d21-9c34-7a2e6f0b1d55",
    "virtual_number": "918071387146",
    "party_a": "9876543210",
    "party_b": "9123456789",
    "reference": "ORD-4821",
    "status": "active",
    "expires_at": "2026-07-14T18:30:00Z",
    "total_calls": 0,
    "created_at": "2026-07-14T18:00:00Z"
  }
}
```

Give `virtual_number` to both parties. Track the session with `session_id`.

**`422` — No free virtual number, the same party twice, or the reference is already active with different parties**

```json
{
  "error": {
    "code": "session_error",
    "message": "all 1 enrolled masking numbers are busy for these parties; enroll more numbers to add capacity"
  }
}
```

When every enrolled number is already busy for these parties, [enroll more numbers](https://edesy.in/docs/number-masking/sessions/enroll-numbers) to add concurrency capacity.

See all [error codes](https://edesy.in/docs/number-masking/api-reference/errors).
