# Quickstart

> Create a personal access token, make your first request to the Reassign REST API, read a day, and add an event.

Source: https://reassign.dev/quickstart/

This guide shows how to make your first requests to the Reassign REST API. You create a token, read a day, add an event, and then undo the change.

> This guide needs a trial or Pro plan. The 7-day free trial includes Reassign
> Pro.

## 1. Create a token

Create a personal access token (a "key") in the Reassign app.

1. Sign in to the Reassign app.
2. Go to **Settings** › **Integrations** › **API**.
3. In **New key**, type a name, for example "My app".
4. Choose **Read only** or **Read and write**. For this guide, choose **Read and write**, because step 4 writes an event.
5. Click **Create key**.
6. Copy the key from **Your new key**, and keep it in a safe place.
7. Click **I saved it**.

| Choice         | Scopes                     | Permits              |
| -------------- | -------------------------- | -------------------- |
| Read only      | `events:read`              | Read calls only      |
| Read and write | `events:read events:write` | Read and write calls |

- The key starts with `rsk_live_`.
- The app shows the key one time only. If you lose it, revoke it and create a new key.
- Keep the key on your server. Do not put it in browser code.

Refer to [Authentication](https://reassign.dev/authentication.md) for OAuth 2.0 and for the revoke steps.

## 2. Test the token

Call `GET /me`. This call shows who owns the token, the plan, and the scopes. It works on all plans.

**Request** — **GET** `/api/v1/me`

**cURL**

```bash
curl "https://reassign.app/api/v1/me" \
  -H "Authorization: Bearer rsk_live_YOUR_TOKEN"
```

**JavaScript**

```js
const res = await fetch("https://reassign.app/api/v1/me", {
  headers: {
    Authorization: "Bearer rsk_live_YOUR_TOKEN",
  },
});
const data = await res.json();
```

**Response**

```json
{
  "id": "00000000-0000-4000-8000-000000000042",
  "email": "user@example.com",
  "plan": "pro",
  "trialEndsAt": null,
  "offer": {
    "kind": "entitled"
  },
  "subscription": {
    "provider": "web",
    "status": "active",
    "currentPeriodEndsAt": "2026-09-13T08:00:00.000Z",
    "cancelAtPeriodEnd": false
  },
  "emailSubscribed": true,
  "scopes": [
    "events:read",
    "events:write"
  ],
  "auth": {
    "via": "oauth",
    "clientId": "reassign-ios"
  }
}
```

- `plan` is `"trial"`, `"pro"`, or `"none"`.
- `trialEndsAt` is an instant, for example `"2026-09-30T08:00:00.000Z"`, on a trial. It is `null` on all other plans.
- `email` is `null` when the account has no email address.
- The example shows an OAuth 2.0 token. For a personal access token, `auth` is `{ "via": "pat" }`.
- If the token is not valid, the API returns HTTP 401. Refer to [Errors](https://reassign.dev/errors.md).

## 3. Read a day

Call `GET /schedule` with `from` and `to`. For one day, send the same date in the two parameters.

**Request** — **GET** `/api/v1/schedule`

**cURL**

```bash
curl "https://reassign.app/api/v1/schedule?from=2026-08-14&to=2026-08-14" \
  -H "Authorization: Bearer rsk_live_YOUR_TOKEN"
```

**JavaScript**

```js
const res = await fetch("https://reassign.app/api/v1/schedule?from=2026-08-14&to=2026-08-14", {
  headers: {
    Authorization: "Bearer rsk_live_YOUR_TOKEN",
  },
});
const data = await res.json();
```

**Response**

```json
{
  "timezone": "Europe/Ljubljana",
  "now": "2026-08-14T10:42",
  "days": [
    {
      "date": "2026-08-14",
      "events": [
        {
          "id": "b7f1c2a0-1d2e-4f3a-9b8c-5d6e7f801234",
          "start": "2026-08-14T09:00",
          "end": "2026-08-14T10:30",
          "kind": "blocking",
          "name": "Team sync",
          "source": "google",
          "meeting": {
            "url": "https://us02web.zoom.us/j/1234567890",
            "label": "Zoom"
          },
          "location": {
            "text": "Room 4, 2nd floor",
            "url": "https://www.google.com/maps/search/?api=1&query=Room%204%2C%202nd%20floor"
          },
          "areaId": "3c9d2a41-6b7e-4f80-9a1b-2c3d4e5f6071",
          "activityTypeId": "5e6f7081-92a3-4b4c-8d5e-6f708192a3b4",
          "projectId": null,
          "priority": 0,
          "calendarId": "8a1f0c2e-3b4d-4e5f-9a0b-1c2d3e4f5061",
          "mirrorCalendarIds": [
            "a1b2c3d4-5e6f-4071-8293-a4b5c6d7e8f9"
          ]
        }
      ],
      "freeSlots": [
        {
          "start": "2026-08-14T10:45",
          "end": "2026-08-15T00:00"
        }
      ],
      "loadByArea": [
        {
          "areaId": "3c9d2a41-6b7e-4f80-9a1b-2c3d4e5f6071",
          "minutes": 90,
          "eventCount": 1
        }
      ],
      "loadByActivityType": [
        {
          "activityTypeId": "5e6f7081-92a3-4b4c-8d5e-6f708192a3b4",
          "minutes": 90,
          "eventCount": 1
        }
      ],
      "loadByProject": [
        {
          "projectId": null,
          "minutes": 90,
          "eventCount": 1
        }
      ]
    }
  ],
  "areas": [
    {
      "id": "3c9d2a41-6b7e-4f80-9a1b-2c3d4e5f6071",
      "name": "Work",
      "color": "#3b82f6"
    }
  ],
  "activityTypes": [
    {
      "id": "5e6f7081-92a3-4b4c-8d5e-6f708192a3b4",
      "name": "Focus",
      "pattern": "solid"
    }
  ],
  "projects": [],
  "userPreferences": {
    "conflictPolicy": "propose"
  },
  "integrations": {
    "aiClassify": true,
    "defaultCalendarId": "8a1f0c2e-3b4d-4e5f-9a0b-1c2d3e4f5061",
    "sources": [
      {
        "provider": "google",
        "account": "user@example.com",
        "status": "connected",
        "calendars": [
          {
            "id": "8a1f0c2e-3b4d-4e5f-9a0b-1c2d3e4f5061",
            "name": "Work",
            "writable": true,
            "area": {
              "mode": "automatic"
            },
            "activityType": {
              "mode": "automatic"
            },
            "kind": {
              "mode": "automatic"
            }
          },
          {
            "id": "a1b2c3d4-5e6f-4071-8293-a4b5c6d7e8f9",
            "name": "Team",
            "writable": true,
            "area": {
              "mode": "automatic"
            },
            "activityType": {
              "mode": "automatic"
            },
            "kind": {
              "mode": "automatic"
            }
          }
        ]
      }
    ]
  },
  "backlogCount": 1
}
```

- `from` and `to` are required. Send each as `"YYYY-MM-DD"`. The range can be 366 days or less.
- `days` has one item for each date. Each day has its `events` and its free time (`freeSlots`).
- `start` and `end` are local datetimes (`"YYYY-MM-DDTHH:MM"`) in the time zone of the account. The response gives the zone one time, in `timezone`.
- An event ends on a later date when it goes past midnight. Refer to [Core concepts](https://reassign.dev/concepts.md).
- `areaId`, `activityTypeId`, and `projectId` are ids. Find their names in the `areas`, `activityTypes`, and `projects` lists. The value is `null` when the event has none.
- `priority` is a whole number from `0` (none) to `4` (urgent).
- `now` is the current local datetime of the account.
- A query parameter that the route does not know gets HTTP 422 with the error code `validation`.
- Refer to [Scheduling](https://reassign.dev/reference/scheduling.md) for all fields.

> `GET /events` does not read a day. It is a change poll: it returns the events
> that changed after your last poll. Refer to [Events](https://reassign.dev/reference/events.md).

## 4. Add an event

Call `POST /events` with an `ops` array. Each op is one change. This call needs the `events:write` scope.

The example has three ops: a `create`, an `update` of one occurrence, and a `delete` of one occurrence. For your first call, send only the `create` op. Use an `areaId` and an `activityTypeId` from step 3, or remove them. Remove `calendarId` if you have no connected calendar.

**Request** — **POST** `/api/v1/events`

**cURL**

```bash
curl -X POST "https://reassign.app/api/v1/events" \
  -H "Authorization: Bearer rsk_live_YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
  "ops": [
    {
      "op": "create",
      "name": "Deep work",
      "start": "2026-08-14T09:00",
      "end": "2026-08-14T10:30",
      "areaId": "3c9d2a41-6b7e-4f80-9a1b-2c3d4e5f6071",
      "activityTypeId": "5e6f7081-92a3-4b4c-8d5e-6f708192a3b4",
      "notes": "Draft the launch post.",
      "calendarId": "8a1f0c2e-3b4d-4e5f-9a0b-1c2d3e4f5061"
    },
    {
      "op": "update",
      "id": "c4d5e6f7-0819-4a2b-8c3d-4e5f60718293@2026-08-15",
      "start": "2026-08-15T16:00"
    },
    {
      "op": "delete",
      "id": "c4d5e6f7-0819-4a2b-8c3d-4e5f60718293@2026-08-16"
    }
  ]
}'
```

**JavaScript**

```js
const res = await fetch("https://reassign.app/api/v1/events", {
  method: "POST",
  headers: {
    Authorization: "Bearer rsk_live_YOUR_TOKEN",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "ops": [
      {
        "op": "create",
        "name": "Deep work",
        "start": "2026-08-14T09:00",
        "end": "2026-08-14T10:30",
        "areaId": "3c9d2a41-6b7e-4f80-9a1b-2c3d4e5f6071",
        "activityTypeId": "5e6f7081-92a3-4b4c-8d5e-6f708192a3b4",
        "notes": "Draft the launch post.",
        "calendarId": "8a1f0c2e-3b4d-4e5f-9a0b-1c2d3e4f5061"
      },
      {
        "op": "update",
        "id": "c4d5e6f7-0819-4a2b-8c3d-4e5f60718293@2026-08-15",
        "start": "2026-08-15T16:00"
      },
      {
        "op": "delete",
        "id": "c4d5e6f7-0819-4a2b-8c3d-4e5f60718293@2026-08-16"
      }
    ]
  }),
});
const data = await res.json();
```

**Response**

```json
{
  "timezone": "Europe/Ljubljana",
  "undoToken": "00000000-0000-4000-8000-000000000001",
  "expiresAt": "2026-08-13T09:12:00.000Z",
  "results": [
    {
      "index": 0,
      "status": "ok",
      "result": {
        "created": {
          "id": "b7f1c2a0-1d2e-4f3a-9b8c-5d6e7f801234",
          "start": "2026-08-14T09:00",
          "end": "2026-08-14T10:30",
          "kind": "blocking",
          "name": "Deep work",
          "notes": "Draft the launch post.",
          "source": "reassign",
          "areaId": "3c9d2a41-6b7e-4f80-9a1b-2c3d4e5f6071",
          "activityTypeId": "5e6f7081-92a3-4b4c-8d5e-6f708192a3b4",
          "projectId": null,
          "priority": 0,
          "calendarId": "8a1f0c2e-3b4d-4e5f-9a0b-1c2d3e4f5061"
        }
      }
    },
    {
      "index": 1,
      "status": "ok",
      "result": {
        "updated": {
          "id": "c4d5e6f7-0819-4a2b-8c3d-4e5f60718293@2026-08-15",
          "seriesId": "c4d5e6f7-0819-4a2b-8c3d-4e5f60718293",
          "originalDate": "2026-08-15",
          "start": "2026-08-15T16:00",
          "end": "2026-08-15T17:30",
          "kind": "blocking",
          "name": "Deep work",
          "recurrence": "FREQ=DAILY",
          "source": "reassign",
          "areaId": "3c9d2a41-6b7e-4f80-9a1b-2c3d4e5f6071",
          "activityTypeId": "5e6f7081-92a3-4b4c-8d5e-6f708192a3b4",
          "projectId": null,
          "priority": 0
        }
      }
    },
    {
      "index": 2,
      "status": "ok",
      "result": {
        "deletedIds": [
          "c4d5e6f7-0819-4a2b-8c3d-4e5f60718293@2026-08-16"
        ]
      }
    }
  ]
}
```

- `results` has one row for each op, in the same order. `results[0].result.created` is the new event.
- Keep the `undoToken`. You use it in the next step. It expires at `expiresAt`.
- `start` and `end` are local datetimes. An event that you write with `POST /events` is 5 minutes to 168 hours long. The server keeps the exact minute.
- The batch is atomic by default. If one op fails, the server applies none of them.
- If the time overlaps a blocking event, the op fails with the error code `conflict`. The error row can give up to three `nearestSlots`. When all failed ops have the code `conflict`, the HTTP status is 409.
- A bad op, for example an op with an unknown key, becomes an error row with the code `validation`.
- A token with only `events:read` gets HTTP 403 with the error code `scope`, also when the body is not correct.

Refer to [Errors](https://reassign.dev/errors.md) for the rejected batch.

## 5. Undo the change

Send the `undoToken` to `POST /actions/undo`. The API removes the event that you added.

**Request** — **POST** `/api/v1/actions/undo`

**cURL**

```bash
curl -X POST "https://reassign.app/api/v1/actions/undo" \
  -H "Authorization: Bearer rsk_live_YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
  "tokens": [
    "00000000-0000-4000-8000-000000000001"
  ]
}'
```

**JavaScript**

```js
const res = await fetch("https://reassign.app/api/v1/actions/undo", {
  method: "POST",
  headers: {
    Authorization: "Bearer rsk_live_YOUR_TOKEN",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "tokens": [
      "00000000-0000-4000-8000-000000000001"
    ]
  }),
});
const data = await res.json();
```

**Response**

```json
{
  "results": [
    {
      "index": 0,
      "status": "ok",
      "result": {
        "reverted": 1,
        "restored": 1,
        "removed": 0,
        "voided": 0
      }
    }
  ]
}
```

- The example response restores one event (`restored` is 1), for example after a delete. For the undo of a create, `removed` is 1 and `restored` is 0. `reverted` is the total of the counts.
- An undo token is valid for 30 minutes from the write.
- You can use each token one time only.
- To remove an event after the 30 minutes, send `POST /events` with `{ "op": "delete", "id": "<event id>" }`. Refer to [Events](https://reassign.dev/reference/events.md).

## What's next

- [Core concepts](https://reassign.dev/concepts.md): the day plan, spans, recurring events, and undo.
- [Events](https://reassign.dev/reference/events.md): all event fields and calls.
- [Connect an AI assistant](https://reassign.dev/mcp.md): use Reassign from an AI assistant through MCP.
