> For the complete documentation index, see [llms.txt](https://mava.gitbook.io/mava-docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://mava.gitbook.io/mava-docs/webhooks-and-api/api/create-ticket.md).

# Create Ticket

### Endpoint

```
POST https://gateway.mava.app/api/ticket
```

### Authentication

Authentication requires an API token passed via the `x-auth-token` header. Generate API keys in your dashboard under **Admin → API & Webhooks → API Keys**.

**Rate Limit:** 90 requests per 30 seconds per API key. Exceeding this threshold returns `429 Too Many Requests`.

```
x-auth-token: YOUR_API_KEY
Content-Type: application/json
```

### Request Body

Provide exactly one of `customerId` or `customer`. All other parameters are optional with sensible defaults.

| Field                    | Type      | Required                 | Description                                                                                                                        |
| ------------------------ | --------- | ------------------------ | ---------------------------------------------------------------------------------------------------------------------------------- |
| `customerId`             | string    | Conditional              | ObjectId of an existing customer. Mutually exclusive with `customer`.                                                              |
| `customer`               | object    | Conditional              | Customer details for find-or-create by email or wallet. Mutually exclusive with `customerId`. Requires `email` or `walletAddress`. |
| `customer.name`          | string    | No                       | Display name (applied only on creation).                                                                                           |
| `customer.email`         | string    | No\*                     | Lookup/creation identifier. Required if `walletAddress` absent.                                                                    |
| `customer.walletAddress` | string    | No\*                     | EVM address (`0x` + 40 hex). Required if `email` absent.                                                                           |
| `customer.attributes`    | array     | No                       | Up to 30 custom attributes: `{ "attribute": "<id>", "content": "<value>" }`                                                        |
| `status`                 | string    | No                       | Initial ticket status. Default: `"Open"`.                                                                                          |
| `priority`               | number    | No                       | `1` = Low, `2` = Medium, `3` = High. Default: `2`.                                                                                 |
| `sourceType`             | string    | No                       | Origin source. Default: `"web"`.                                                                                                   |
| `assignedTo`             | string    | No                       | ID of the agent to assign (from GET /agents).                                                                                      |
| `category`               | string    | No                       | ObjectId of a ticket category.                                                                                                     |
| `tags`                   | string\[] | No                       | Tag ObjectIds (max 20).                                                                                                            |
| `emailSubjectLine`       | string    | No                       | Email subject (max 998 chars).                                                                                                     |
| `attributes`             | array     | No                       | Up to 30 ticket attributes: `{ "attribute": "<id>", "content": "<value>" }`                                                        |
| `message`                | object    | No                       | Optional initial customer message.                                                                                                 |
| `message.content`        | string    | Yes (if message present) | Message text (max 10,000 chars).                                                                                                   |
| `message.attachments`    | array     | No                       | Pre-uploaded file attachments (max 10).                                                                                            |
| `silent`                 | boolean   | No                       | When true, suppresses notifications and automations. Default: `false`.                                                             |
| `notify`                 | boolean   | No                       | In-app notification for assigned agent. Default: `true`.                                                                           |
| `triggerAutomations`     | boolean   | No                       | Run automations with initial message. Default: `true`.                                                                             |

> **Note:** `assignedTo` takes an agent **ID** (obtained from GET /agents), not an email address. This allows agents who signed up with a wallet — and therefore have no email — to be assigned tickets. An ID that does not belong to your organization is rejected with `ASSIGNEE_NOT_FOUND`.

#### Valid Values

* **`status`**: `"Open"`, `"Pending"`, `"Resolved"`, `"Waiting"`
* **`priority`**: `1`, `2`, `3`
* **`sourceType`**: `"web"`, `"discord"`, `"telegram"`, `"twitter"`, `"email"`

#### Attachments

Files must be pre-uploaded to Mava-approved storage. The endpoint validates URLs against an allow-list and rejects external URLs.

| Field      | Type   | Required | Description                                                                     |
| ---------- | ------ | -------- | ------------------------------------------------------------------------------- |
| `url`      | string | Yes      | HTTPS URL in your Mava S3 bucket.                                               |
| `fileName` | string | Yes      | Original filename.                                                              |
| `fileSize` | number | No       | File size in bytes.                                                             |
| `type`     | string | No       | Type enum: `"Image"`, `"Video"`, `"Audio"`, `"Voice"`, `"Animation"`, `"File"`. |

#### Customer Resolution

When providing a `customer` object, the system looks up existing customers in your organization by: (1) `email` if provided, otherwise (2) `walletAddress`. If no match exists, a new customer is created. Existing customers are reused without overwriting `name` or `attributes`.

### Response

**Success — `201 Created`**

Returns the fully populated ticket with all fields:

```json
{
  "ticket": {
    "_id": "645xyz...",
    "customer": {
      "_id": "645abc...",
      "name": "Jane Doe",
      "email": "jane@example.com"
    },
    "client": "645org...",
    "status": "Open",
    "priority": 2,
    "sourceType": "web",
    "assignedTo": null,
    "category": null,
    "tags": [],
    "attributes": [],
    "aiStatus": "None",
    "isVisible": true,
    "messages": [
      {
        "_id": "645msg...",
        "messageType": "ExternalMessage",
        "content": "Hello, I need help with my order.",
        "senderType": "customer",
        "senderName": null,
        "attachments": [],
        "createdAt": "2026-05-14T12:00:00.000Z"
      }
    ],
    "createdAt": "2026-05-14T12:00:00.000Z",
    "updatedAt": "2026-05-14T12:00:00.000Z"
  }
}
```

#### **Error Responses**

All non-2xx responses include a stable, uppercase `error` code and optional `message`:

| Status | Error Code               | Condition                                                      |
| ------ | ------------------------ | -------------------------------------------------------------- |
| `400`  | `INVALID_CUSTOMER_MODE`  | Both or neither customer mode sent, or missing email/wallet.   |
| `400`  | `VALIDATION_ERROR`       | Field validation failed (type, range, length, etc.).           |
| `400`  | `INVALID_ATTACHMENT_URL` | URL outside allow-list, non-HTTPS, or path traversal detected. |
| `400`  | `ASSIGNEE_NOT_FOUND`     | Agent ID does not exist.                                       |
| `401`  | —                        | Missing or invalid token.                                      |
| `404`  | `CUSTOMER_NOT_FOUND`     | Customer ID not found or belongs to another org.               |
| `404`  | `ASSIGNEE_NOT_FOUND`     | Agent ID not found or in different org.                        |
| `404`  | `CATEGORY_NOT_FOUND`     | Category not found or in different org.                        |
| `404`  | `TAG_NOT_FOUND`          | One or more tags not found or in different org.                |
| `429`  | —                        | Rate limit exceeded.                                           |
| `500`  | `INTERNAL_ERROR`         | Server error; request rolls back completely.                   |

Error format:

```json
{
  "error": "VALIDATION_ERROR",
  "message": "body/priority must be equal to one of the allowed values"
}
```

### Behavior

* **Atomicity** — All operations (customer creation, ticket creation, messaging, notifications) execute within a single transaction. Failure rolls back the entire request.
* **Tenant Isolation** — All ObjectId references are verified against your organization. Cross-org references return `404`.
* **Empty Tickets** — Tickets without messages won't appear in the default "Open" filter but remain visible in Global space.
* **Silent Override** — Setting `silent: true` forces both `notify` and `triggerAutomations` to `false`.

### Code Examples

**Curl — Minimal with Existing Customer**

```bash
curl -X POST https://gateway.mava.app/api/ticket \
  -H "x-auth-token: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "customerId": "645abc1234567890abcdef12"
  }'
```

**Curl — Find-or-Create by Email with Message**

```bash
curl -X POST https://gateway.mava.app/api/ticket \
  -H "x-auth-token: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "customer": {
      "email": "jane@example.com",
      "name": "Jane Doe"
    },
    "priority": 3,
    "message": {
      "content": "I cannot log in to my account."
    }
  }'
```

**Curl — Full Payload with Assignment, Category, Tags, and Attachment**

```bash
curl -X POST https://gateway.mava.app/api/ticket \
  -H "x-auth-token: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "customerId": "645abc1234567890abcdef12",
    "status": "Open",
    "priority": 3,
    "sourceType": "email",
    "assignedTo": "645ghi1234567890abcdef34",
    "category": "645jkl1234567890abcdef56",
    "tags": ["645mno1234567890abcdef78"],
    "emailSubjectLine": "Refund request — order #1234",
    "message": {
      "content": "Please see attached receipt.",
      "attachments": [
        {
          "url": "https://s3.eu-central-1.amazonaws.com/app.mava.container.2/receipt.jpg",
          "fileName": "receipt.jpg",
          "fileSize": 30106,
          "type": "Image"
        }
      ]
    }
  }'
```

> The `assignedTo` value above (`645ghi1234567890abcdef34`) is an agent **ID** from GET /agents, not an email.

**Node.js — Create and Retrieve Ticket ID**

```javascript
const response = await fetch('https://gateway.mava.app/api/ticket', {
  method: 'POST',
  headers: {
    'x-auth-token': process.env.MAVA_API_KEY,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    customer: {
      email: 'jane@example.com',
      name: 'Jane Doe',
    },
    message: {
      content: 'Hello, I need help with my order.',
    },
  }),
});

if (!response.ok) {
  const { error, message } = await response.json();
  throw new Error(`Mava create-ticket failed: ${error} — ${message ?? ''}`);
}

const { ticket } = await response.json();
console.log('Created ticket:', ticket._id);
```

**Node.js — Silent Backfill**

```javascript
await fetch('https://gateway.mava.app/api/ticket', {
  method: 'POST',
  headers: {
    'x-auth-token': process.env.MAVA_API_KEY,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    customer: { email: 'legacy@example.com' },
    status: 'Resolved',
    sourceType: 'email',
    message: { content: 'Imported from legacy system.' },
    silent: true,
  }),
});
```
