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

# Tickets

> POST /api/sdk/tickets/create -- Create a support ticket programmatically.

## Endpoint

```
POST /api/sdk/tickets/create
```

Create a support ticket with proper user association. Designed for third-party integrations like form builders, CRMs, bug reporters, and webhook handlers that need to submit structured data to your Halo inbox.

Unlike sending a raw email (which may lose user identity when the sender is a notification service like `noreply@app.com`), this endpoint lets you explicitly specify who the ticket is from.

## Authentication

Requires your publishable widget key in the `Authorization` header:

```
Authorization: Bearer ab_live_xxxxxxxxxxxxxxxx
```

The `org_id` is derived from the widget key.

> [!NOTE]
>
> Use your widget key from **Setup > Install**. It is publishable (safe in browser code). Enable [Identity Verification](https://docs.haloagents.ai/advanced/identity-verification.md) when scoping requests by `user_id`.

## Request Body

### User Identification

At least one of `user_id` or `email` is required. If the user doesn't exist yet, they are created automatically.

> [!NOTE]
>
> When identity verification is **enforced** for your workspace, `user_id` plus a valid `user_token` (or `user_hash`) is required. The email-only path returns HTTP 403 in that mode, because the widget key is public and an unverified email could open tickets on any contact's record. See [Identity Verification](https://docs.haloagents.ai/advanced/identity-verification.md).

**user\_id** `string`

Your system's unique identifier for this user. If provided, Halo looks up the user by `external_id` first.

**email** `string`

The user's email address. Used as a fallback lookup if `user_id` doesn't match, or as the primary identifier if `user_id` is omitted. Also used for email notifications and AI agent replies.

**name** `string`

The user's display name. Used when creating a new user record, or to enrich context if the user already exists.

### Ticket Content

**subject** `string` *required*

The ticket subject line. This appears as the ticket title in your inbox. Max 1,000 characters.

**message** `string`

The full ticket body / description. If omitted, the `subject` is used as the initial message. Max 50,000 characters.

**metadata** `object`

Arbitrary key-value data attached to the ticket. Visible to your team in the dashboard and accessible to the AI agent. Max 50,000 bytes. Use this for structured form data, severity levels, categories, etc. It is stored under the ticket's `metadata.api_metadata` key, so it never overwrites system fields.

### Options

**source** `string` (default: api)

A label identifying where the ticket came from (e.g., `"contact_form"`, `"bug_report"`, `"webhook"`). Max 50 characters. The ticket's channel is set to one of `chat`, `support`, `sales`, `help_center`, `in_app_message`, or `api`. Any other value, including `"email"`, is recorded as `api`, and your original label is kept in the ticket's `metadata.requested_source`. Email-channel tickets cannot be created through this endpoint.

**priority** `string` (default: normal)

Set to `"priority"` to mark the ticket as high priority. Otherwise defaults to `"normal"` (open status).

**ai\_enabled** `boolean` (default: false)

When `true`, the AI agent is triggered to respond to this ticket automatically (using your configured email AI agent). Requires AI email auto-reply to be enabled in your settings. Only honoured for a **verified** user (identity verification enforced, valid `user_token`/`user_hash`). The AI replies to the email already stored on that user's record, never to an `email` passed in the request body. Otherwise the flag is ignored and your team replies from the inbox.

## Example

```bash cURL theme={null}
curl -X POST https://api.haloagents.ai/api/sdk/tickets/create \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer ab_live_xxxxxxxxxxxxxxxx" \
  -d '{
    "email": "jane@acme.com",
    "name": "Jane Doe",
    "subject": "Bug: SEO settings missing from Share page",
    "message": "The SEO settings were previously available on the Share page in the Form Editor but are now missing. I was initially able to edit SEO settings, but the option has disappeared.",
    "metadata": {
      "severity": "major",
      "area": "Form Editor",
      "browser": "Chrome 122",
      "plan": "pro"
    },
    "source": "bug_report",
    "priority": "priority"
  }'
```

```typescript Node.js theme={null}
const response = await fetch(
  "https://api.haloagents.ai/api/sdk/tickets/create",
  {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      Authorization: "Bearer ab_live_xxxxxxxxxxxxxxxx",
    },
    body: JSON.stringify({
      email: "jane@acme.com",
      name: "Jane Doe",
      subject: "Bug: SEO settings missing from Share page",
      message:
        "The SEO settings were previously available on the Share page but are now missing.",
      metadata: {
        severity: "major",
        area: "Form Editor",
        browser: "Chrome 122",
        plan: "pro",
      },
      source: "bug_report",
      priority: "priority",
    }),
  }
);

const data = await response.json();
console.log(data.ticket.id); // Ticket UUID
```

```python Python theme={null}
import requests

response = requests.post(
    "https://api.haloagents.ai/api/sdk/tickets/create",
    headers={
        "Content-Type": "application/json",
        "Authorization": "Bearer ab_live_xxxxxxxxxxxxxxxx",
    },
    json={
        "email": "jane@acme.com",
        "name": "Jane Doe",
        "subject": "Bug: SEO settings missing from Share page",
        "message": "The SEO settings were previously available on the Share page but are now missing.",
        "metadata": {
            "severity": "major",
            "area": "Form Editor",
            "browser": "Chrome 122",
            "plan": "pro",
        },
        "source": "bug_report",
        "priority": "priority",
    },
)

data = response.json()
print(data["ticket"]["id"])
```

**Response (201 Created):**

```json theme={null}
{
  "ticket": {
    "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "status": "priority",
    "subject": "Bug: SEO settings missing from Share page",
    "created_at": "2026-03-17T10:30:00.000Z"
  }
}
```

The response contains only the created ticket. The resolved user record is not echoed back.

## User Resolution Logic

The endpoint resolves users in this order:

1. **By `user_id`** -- looks up `end_users.external_id` matching your `user_id`
2. **By `email`** -- falls back to `end_users.email` lookup
3. **Auto-create** -- if no match is found, creates a new end user with the provided `email`, `name`, and `user_id`

This means you can use the same endpoint whether the user already exists in Halo or not. If you've previously called [identify](https://docs.haloagents.ai/api-reference/users.md), the ticket is linked to that same user record.

## What Happens After Creation

1. The ticket appears in your **Halo inbox** immediately
2. Your team is notified via **Slack** and **email** (if configured)
3. If `ai_enabled` is `true`, the user is verified, and your AI email agent is configured, the AI processes the ticket and replies to the email stored on the user's record
4. The user record is created or linked, so the AI has full context for future interactions

## When to Use This vs Email

| Scenario | Use Tickets API | Use Email |
| - | - | - |
| Form builder (Typeform, OrbitForms, etc.) | Yes -- preserves user identity | Notification emails lose the sender |
| CRM webhook (HubSpot, Salesforce) | Yes -- structured data | Email loses structure |
| Bug report tool | Yes -- include metadata | Subject line only |
| Customer emails you directly | No | Yes -- native email threading |
| Internal forwarding from Gmail/Outlook | No | Yes -- use [email forwarding](https://docs.haloagents.ai/channels/receiving-emails.md) |

## Error Responses

| Status | Error | Cause |
| - | - | - |
| `400` | `subject is required` | Missing or empty `subject` field |
| `400` | `At least one of user_id or email is required` | No user identification provided |
| `403` | `Identity verification is enforced for this org: ...` | Identity verification is enforced and the request has no `user_id` with a valid `user_token`/`user_hash` |
| `401` | `Missing API key` | No `Authorization` header |
| `401` | `Invalid or revoked API key` | Bad or revoked API key |
| `500` | `Failed to create ticket` | Server error during creation |


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.