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

# Create tickets via Form API

> Push tickets into Typewise from Salesforce, internal tools, or any system that makes HTTP requests.

Create tickets in Typewise by posting to the Form API. Use this when an external
system (Salesforce, an internal tool, a webhook) needs to open a ticket
programmatically.

<Info>
  You need an **API key**. Get it from [Behavior → Channels → Email →
  Settings](https://platform.typewise.app/channels/email/settings).
</Info>

<Note>
  If you have a backend, send requests from there to keep your API key private.
  Client-side usage (e.g. from Webflow) is possible, but it exposes your bearer
  API key in the browser. Prefer a backend or proxy in production. If you suspect
  the key is exposed, regenerate it immediately.
</Note>

***

## Endpoint

```
POST https://platform-api.typewise.app/api/emails/form
```

**Authentication** (use either):

* Header: `Authorization: Bearer YOUR_API_KEY`
* Query parameter: `?apiKey=YOUR_API_KEY`

***

## Quick start

```js theme={null}
const API_KEY = "YOUR_API_KEY";
const BASE_URL = "https://platform-api.typewise.app/api/emails/form";

const form = new FormData();
form.append("message", "Customer message body here");
form.append("email", "customer@example.com");
form.append("variables.subject", "Order issue #12345");
form.append("variables.name", "Jane Doe");

fetch(BASE_URL, {
  method: "POST",
  headers: { Authorization: `Bearer ${API_KEY}` },
  body: form,
})
  .then(async (response) => {
    const body = await response.json();
    console.log(response.status, body);
  })
  .catch((error) => {
    console.error("Error:", error);
  });
```

<Note>
  `application/json` is also accepted if you prefer, send a JSON body with
  `message`, `email`, and a `variables` object. FormData is recommended because
  it also supports file attachments.
</Note>

### Response

**Success** (200):

```json theme={null}
{ "success": true }
```

**Errors:**

| Status | Body                          | Cause                                |
| :----- | :---------------------------- | :----------------------------------- |
| 401    | `{ "error": "Unauthorized" }` | Missing API key                      |
| 500    | Server error                  | Invalid or expired API key           |
| 400    | Validation error              | Missing `message` or invalid `email` |
| 429    | Rate limit                    | Too many requests                    |

***

## Fields

### Required

| Field     | Description                |
| :-------- | :------------------------- |
| `message` | The ticket body            |
| `email`   | End customer email address |

### Recommended

| Field               | Description                                             |
| :------------------ | :------------------------------------------------------ |
| `variables.subject` | Ticket subject line (shown in portal and email threads) |
| `variables.name`    | Customer display name                                   |

### Attachments

Append one or more files as `attachments` fields:

```js theme={null}
form.append("attachments", file1, "invoice.pdf");
form.append("attachments", file2, "receipt.pdf");
```

### Custom variables

Any field prefixed with `variables.` is stored on the ticket and visible to
agents in the portal.

```js theme={null}
form.append("variables.orderId", "ORD-98765");
form.append("variables.priority", "high");
```

***

## Salesforce integration

Link Typewise tickets to Salesforce cases so resolution syncs back to your CRM.

<Note>
  The Form API works without Salesforce connected. To sync resolved tickets back
  to Salesforce, connect it via [Salesforce
  OAuth](/documentation/integrations/supported/salesforce-oauth).
</Note>

### Case ID

Pass your Salesforce Case ID (18-character format) as `variables.crmTicketId`.
This field controls what happens when the ticket resolves:

* **With** `crmTicketId`, Typewise updates the existing Salesforce case
* **Without** `crmTicketId`, Typewise creates a new Salesforce case

### Display fields

These variables appear in the agent portal for reference. They don't affect sync.

| Field                              | Example              |
| :--------------------------------- | :------------------- |
| `variables.Salesforce Ticket ID`   | `500d200002SGATiAAP` |
| `variables.Salesforce Case Number` | `00001772`           |

### Example

Add these fields to the [quick start](#quick-start) script:

```js theme={null}
const SF_CASE_ID = "500d200002SGATiAAP";

form.append("variables.crmTicketId", SF_CASE_ID);
form.append("variables.Salesforce Ticket ID", SF_CASE_ID);
form.append("variables.Salesforce Case Number", "00001772");
form.append("variables.subject", "Case: Billing inquiry #00001772");
```

***

## See also

* [Set up email](/guides/channels/setup-email), configure email sending and
  receiving
* [Salesforce OAuth](/documentation/integrations/supported/salesforce-oauth).   connect Salesforce as an integration
