> For the complete documentation index, see [llms.txt](https://docs.shortcutco.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.shortcutco.com/developers/quickstart.md).

# Quickstart

Create an API token, check it, create a lead, and see the lead in Shortcut, in about five minutes.

After this page you will have an API token, a working request, and a new lead in your Shortcut workspace that you created with the API. You need a terminal with `curl` (or Node.js 18+ or Python with `requests`) and a Shortcut login as a company admin.

## 1. Create a token

1. In Shortcut, open **Settings → API & integrations**.
2. Select **Create token**.
3. Name it after what will use it, for example `Quickstart`.
4. Under scopes, choose **Custom** and turn on **Read customers** and **Write leads**. Write leads lets the token create leads; it also lets it read them. Leave **Trigger automations** off for this guide.
5. Choose an expiry. 30 days is plenty for this guide.
6. Select **Create**, then **Copy**. Shortcut shows the token once. If you lose it, revoke it and create another.

A token looks like `scut_live_k7Qm3pX2Lw9a_…`. It is a password for your workspace: keep it in a secret store or an environment variable, never in a web page or a public repository.

Every address on this page is the production one; for a workspace on Shortcut's dev environment, use the dev addresses in [Environments](/developers/concepts/conventions.md#environments).

Put it in your shell for the rest of this guide:

```bash
export SHORTCUT_TOKEN="scut_live_paste_your_token_here"
```

## 2. Check the token

`GET /v1/me` says which token you are using, what it can do, and which workspace it belongs to.

{% tabs %}
{% tab title="curl" %}

```bash
curl https://api.shortcutco.com/v1/me \
  -H "Authorization: Bearer $SHORTCUT_TOKEN"
```

{% endtab %}

{% tab title="Node.js" %}

```javascript
// quickstart.mjs: Node.js 18 or later
const response = await fetch("https://api.shortcutco.com/v1/me", {
  headers: { Authorization: `Bearer ${process.env.SHORTCUT_TOKEN}` },
});
if (!response.ok) throw new Error(`Shortcut API ${response.status}: ${await response.text()}`);
console.log(await response.json());
```

{% endtab %}

{% tab title="Python" %}

```python
import os

import requests

response = requests.get(
    "https://api.shortcutco.com/v1/me",
    headers={"Authorization": f"Bearer {os.environ['SHORTCUT_TOKEN']}"},
)
response.raise_for_status()
print(response.json())
```

{% endtab %}
{% endtabs %}

```json
{
  "data": {
    "object": "token",
    "token": {
      "id": "7d1c2f0e-5b8a-4f2e-9c11-2a6b9f3e4d10",
      "name": "Quickstart",
      "kind": "api_key",
      "scopes": ["customers:read", "leads:write"]
    },
    "workspace": {
      "id": "3f6a8e2b-1c4d-4e7f-8a9b-0c1d2e3f4a5b",
      "name": "Greenline Lawn Care",
      "timezone": "America/Denver",
      "today": "2026-10-16"
    },
    "rateLimit": { "limit": 600, "remaining": 599, "resetSeconds": 60 }
  }
}
```

A `401` means the token was copied incompletely, was revoked, or has expired. Create a new one and try again.

## 3. Create a lead

`POST /v1/leads` creates a lead that behaves like one from your website form: staff are notified, and automations, which can email or text the lead, start only if the token has **Trigger automations**: the new-lead welcome and the rules your workspace has built in **Automations** alike. For a new email, Shortcut creates a customer account and saves the address as its property. If the email already belongs to one of your customers, the token also needs **Write customers**; without it the request is refused and nothing is created.

Use an email address you can check that is not your Shortcut login. A staff member's email is never linked to a customer account, so `customerId` and `propertyId` would come back `null` instead of as in the example.

{% tabs %}
{% tab title="curl" %}

```bash
curl https://api.shortcutco.com/v1/leads \
  -X POST \
  -H "Authorization: Bearer $SHORTCUT_TOKEN" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: quickstart-lead-1" \
  -d '{
    "firstName": "Robin",
    "lastName": "Quickstart",
    "email": "you@example.com",
    "phone": "(406) 555-0142",
    "address": "412 Elm St, Bozeman, MT 59715",
    "servicesRequested": ["Weekly mowing"],
    "specialRequests": "Corner lot; gate on the side street."
  }'
```

{% endtab %}

{% tab title="Node.js" %}

```javascript
// quickstart.mjs: Node.js 18 or later
const response = await fetch("https://api.shortcutco.com/v1/leads", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.SHORTCUT_TOKEN}`,
    "Content-Type": "application/json",
    "Idempotency-Key": "quickstart-lead-1",
  },
  body: JSON.stringify({
    firstName: "Robin",
    lastName: "Quickstart",
    email: "you@example.com",
    phone: "(406) 555-0142",
    address: "412 Elm St, Bozeman, MT 59715",
    servicesRequested: ["Weekly mowing"],
    specialRequests: "Corner lot; gate on the side street.",
  }),
});
if (!response.ok) throw new Error(`Shortcut API ${response.status}: ${await response.text()}`);
const { data: lead } = await response.json();
```

{% endtab %}

{% tab title="Python" %}

```python
import os

import requests

response = requests.post(
    "https://api.shortcutco.com/v1/leads",
    headers={
        "Authorization": f"Bearer {os.environ['SHORTCUT_TOKEN']}",
        "Idempotency-Key": "quickstart-lead-1",
    },
    json={
        "firstName": "Robin",
        "lastName": "Quickstart",
        "email": "you@example.com",
        "phone": "(406) 555-0142",
        "address": "412 Elm St, Bozeman, MT 59715",
        "servicesRequested": ["Weekly mowing"],
        "specialRequests": "Corner lot; gate on the side street.",
    },
)
response.raise_for_status()
lead = response.json()["data"]
```

{% endtab %}
{% endtabs %}

```json
{
  "data": {
    "id": "c2b9e7a1-4d3f-4b6e-9a8c-1f2e3d4c5b6a",
    "object": "lead",
    "firstName": "Robin",
    "lastName": "Quickstart",
    "companyName": null,
    "email": "you@example.com",
    "phone": "+14065550142",
    "address": "412 Elm St, Bozeman, MT 59715",
    "servicesRequested": ["Weekly mowing"],
    "specialRequests": "Corner lot; gate on the side street.",
    "estimatedHours": null,
    "sqFootage": null,
    "status": "pending",
    "lifecycleStage": "lead",
    "source": "api",
    "customerId": "9a8b7c6d-5e4f-4a3b-8c2d-1e0f9a8b7c6d",
    "propertyId": "1b2c3d4e-5f6a-4b7c-8d9e-0f1a2b3c4d5e",
    "externalReferences": [],
    "createdAt": "2026-10-16T17:04:12.381Z",
    "updatedAt": "2026-10-16T17:04:12.402Z"
  }
}
```

The lead's `source` is `api`. In the app its source reads **API**, so staff can tell it from a lead someone added by hand or one from your website form.

The `Idempotency-Key` header makes the request safe to retry. Send the same request with the same key and you get the same lead back, with the header `Idempotent-Replayed: true`, instead of a second lead. Use a new key, such as a UUID, for each new lead.

## 4. See it in Shortcut

Open **Customers** in Shortcut. Robin Quickstart is at the top of the list, labeled **Lead**, and also under **Needs estimate**. Open it to see the address, the services requested, the special requests, and its source, **API**. Staff who get new-lead notifications were notified.

That's the whole loop: a token, a read, a write, and the result in the app.

## What to do next

* Connect Claude to your workspace with the same kind of token: [Connect Claude](/developers/mcp/connect-claude.md).
* Every response is `{ "data": … }`; errors are `{ "error": { "type", "code", "message", "requestId" } }`. Quote the `requestId` when you contact support.
* Lists return 50 items by default and a `meta.nextCursor` for the next page.
* A workspace can create up to 20 leads a minute through the API; past that the API answers `429` with a `Retry-After` header.
* Revoke the quickstart token in **Settings → API & integrations** when you are done with it.
