> For the complete documentation index, see [llms.txt](https://docs.tiny.plus/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.tiny.plus/api/endpoints/relationship-plans.md).

# Relationship plans

A relationship plan puts one company on a schedule of follow-ups. Each follow-up is a task, usually for the plan's owner, and when someone logs what happened, the plan books the next one. [Relationship Plans](/guides/everyday/relationship-plans.md) describes them as people use them in tiny+.

Plans have endpoints of their own. They aren't a [record type](/api/endpoints/records.md), so the list filters and the generic create, update and delete requests don't apply to them.

| Request                                    | What it does                                                      |
| ------------------------------------------ | ----------------------------------------------------------------- |
| `GET /v2/relationship_plans`               | [Lists plans](#list-plans): the token user's own, or a company's. |
| `GET /v2/relationship_plans/{id}`          | [One plan](#get-one-plan) in full.                                |
| `POST /v2/relationship_plans`              | [Starts a plan](#start-a-plan) on a company.                      |
| `POST /v2/relationship_plans/{id}/touches` | [Logs a follow-up](#log-a-follow-up).                             |
| `PATCH /v2/relationship_plans/{id}`        | [Changes a plan](#change-a-plan), or pauses, resumes or stops it. |
| `GET /v2/relationship_plan_templates`      | [The templates](#templates) a plan can start from.                |

Three things differ from the record endpoints:

* **Ids are numbers**, not strings.
* **Dates are `YYYY-MM-DD` in the account's time zone.** The list of plans carries `today`, the account's own date, so you can tell what is overdue without working out the time zone yourself.
* **Errors are sentences** written for the person using your app: `{"error": "That plan could not be found."}`. See [Errors](#errors).

## Is it switched on?

Plans come with the Pro plan. [`/me`](/api/endpoints/me.md#integrations) carries `integrations.relationship_plans`, which is `true` when the account has them. On an account without them, every request here answers `403`:

```json
{
    "error": "Relationship plans come with the Pro plan."
}
```

## List plans

<mark style="color:blue;">`GET`</mark> `https://api.tiny.plus/v2/relationship_plans`

Without parameters, this lists the live plans the token's user owns or is on the team of.

| Parameter         | Description                                                                                                                             |
| ----------------- | --------------------------------------------------------------------------------------------------------------------------------------- |
| `state`           | `live`, the default, means active and paused. Or `active`, `paused`, `ended` or `all`.                                                  |
| `scope`           | `mine`, the default: plans the token's user owns or is on the team of. `everyone` lists the whole account's, and needs a Records Admin. |
| `company_id`      | Every plan on that company, whoever owns it. The token's user must be able to see the company.                                          |
| `due_within_days` | Only active plans whose next follow-up is due within this many days, overdue ones included. `0` to `366`.                               |
| `due_by`          | The same, up to a date.                                                                                                                 |
| `owner_id`        | Only plans owned by this team member.                                                                                                   |
| `template_id`     | Only plans started from this [template](#templates), or `custom` for plans built from scratch.                                          |
| `limit`, `start`  | 25 plans a page, 100 at most. Pass `next_start` as `start` to get the next page.                                                        |

```bash
curl "https://api.tiny.plus/v2/relationship_plans" \
  -H "Authorization: Bearer {{user_access_token}}"
```

```json
{
    "today": "2026-09-30",
    "total_records": 1,
    "returned_records": 1,
    "start": 0,
    "next_start": null,
    "records": [
        {
            "id": 155147,
            "name": "Warm up a prospect",
            "record_status": "active",
            "status": "on_track",
            "company": {
                "id": 8813,
                "name": "Aardvark Architecture",
                "record_url": "https://crm.tiny.plus/company/8813"
            },
            "owner": { "id": 1231, "name": "Nick Smith" },
            "template": "warm_up_prospect",
            "paused_until": null,
            "next": {
                "task_id": 159220,
                "title": "Send something relevant: Susan Aitchinson",
                "due_date": "2026-10-20",
                "touch_type": "send",
                "step": "Send something relevant",
                "position": "step 3 of 5",
                "assigned_user": { "id": 1231, "name": "Nick Smith" }
            },
            "last_contact_date": "2026-09-29",
            "can_change": true
        }
    ]
}
```

Active plans come first, the soonest next follow-up first, and those with nothing booked last. Paused plans follow, then ended ones. `next_start` is `null` on the last page.

| Field               | Meaning                                                                                                                                                                                   |
| ------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `record_status`     | The plan's state: `active`, `paused`, `finished` (its last step is done) or `stopped`.                                                                                                    |
| `status`            | How a plan is going: `on_track`, `due_soon`, `overdue`, `nothing_booked`, `paused` or `ended`.                                                                                            |
| `template`          | The key of the template the plan started from, or `custom`.                                                                                                                               |
| `paused_until`      | For a paused plan, the day it starts again, or `null` when it waits for someone to resume it.                                                                                             |
| `next`              | The follow-up due soonest, or `null` when the plan is paused, ended or has nothing booked. Its `task_id` is the [task](/api/endpoints/tasks.md#follow-ups-on-a-relationship-plan) to log. |
| `last_contact_date` | The day of the last follow-up logged as done.                                                                                                                                             |
| `can_change`        | Whether the token's user may log and change the plan: its owner, its team and Records Admins may.                                                                                         |

## Get one plan

<mark style="color:blue;">`GET`</mark> `https://api.tiny.plus/v2/relationship_plans/{{id}}`

```bash
curl "https://api.tiny.plus/v2/relationship_plans/{{id}}" \
  -H "Authorization: Bearer {{user_access_token}}"
```

```json
{
    "id": 155147,
    "name": "Warm up a prospect",
    "record_status": "active",
    "status": "on_track",
    "company": { "id": 8813, "name": "Aardvark Architecture", "record_url": "https://crm.tiny.plus/company/8813" },
    "owner": { "id": 1231, "name": "Nick Smith" },
    "team": [
        { "id": 187, "name": "Laurence Persimmon" }
    ],
    "contacts": [
        { "id": 62377, "name": "Susan Aitchinson", "key": true }
    ],
    "goal": "win a new project with them",
    "template": "warm_up_prospect",
    "schedule_from": "done",
    "start_date": "2026-09-27",
    "paused_until": null,
    "stop_reason": null,
    "touches_done": 2,
    "touches_missed": 0,
    "last_contact_date": "2026-09-29",
    "next": { "task_id": 159220, "...": "..." },
    "followups": [
        {
            "task_id": 159220,
            "title": "Send something relevant: Susan Aitchinson",
            "due_date": "2026-10-20",
            "touch_type": "send",
            "step_id": 51,
            "step": "Send something relevant",
            "occurrence": 1,
            "assigned_user": { "id": 1231, "name": "Nick Smith" },
            "contact_ids": [62377],
            "if_done": { "title": "Invite to an event", "due_date": "2026-10-30" }
        }
    ],
    "steps": [
        {
            "id": 50,
            "state": "done",
            "done": 1,
            "title": "Follow-up call",
            "touch_type": "call",
            "anchor": "previous",
            "offset": [1, "week"],
            "repeat": null,
            "times": null,
            "who": "owner",
            "with": "key",
            "notes": ""
        }
    ],
    "touches": [
        {
            "id": 792,
            "date": "2026-09-29",
            "outcome": "done",
            "touch_type": "call",
            "task_id": 155150,
            "step": "Follow-up call",
            "by": { "id": 1231, "name": "Nick Smith" },
            "contacts": [
                { "id": 62377, "name": "Susan Aitchinson" }
            ],
            "notes": "Spoke to Susan today - great chat and plenty more to do next week at conference.",
            "next_date": "2026-10-20",
            "source": null
        }
    ],
    "can_change": true,
    "main_feed": true,
    "offer": null
}
```

A plan read has the fields of a list row, and these:

| Field                            | Meaning                                                                                                                                |
| -------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- |
| `team`, `contacts`               | The team members on the plan besides its owner, and the contacts it is with. `key` marks the key contact.                              |
| `goal`                           | What the plan is for, or `null`.                                                                                                       |
| `schedule_from`                  | `done` when the next follow-up is counted from the day this one was done, or `dates` when the plan keeps to its original dates.        |
| `stop_reason`                    | For a stopped plan: `won_work`, `went_quiet`, `contact_left`, `not_a_fit` or `other`.                                                  |
| `touches_done`, `touches_missed` | How many follow-ups were done, and how many were missed on a plan kept to its dates.                                                   |
| `followups`                      | Every open follow-up, soonest first. `next` is the first of them.                                                                      |
| `steps`                          | The plan's steps, each with its `state`: `waiting`, `active` or `done`.                                                                |
| `touches`                        | The latest 20 things logged on the plan, newest first.                                                                                 |
| `main_feed`                      | Whether notes may also go on the main news feed. See [Log a follow-up](#log-a-follow-up).                                              |
| `offer`                          | Contact logged elsewhere that could count as a follow-up. See [Counting contact logged elsewhere](#counting-contact-logged-elsewhere). |

A follow-up's `contact_ids` are who it is with, and `if_done` is what the plan would book next if it were logged as done today. `if_done` is `null` when the plan ends with this follow-up.

A step's `offset` and `repeat` are a number and a unit: `day`, `week` or `month`. `anchor` is `start` when the step counts from the plan's start date, or `previous` when it waits for the step before it. `who` is `owner`, `turns` (the owner and the team take turns) or `staff:` followed by a team member's id. `with` is `key`, `all` or `turns`.

## Start a plan

<mark style="color:green;">`POST`</mark> `https://api.tiny.plus/v2/relationship_plans`

| Field            | Description                                                                                                                              |
| ---------------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
| `company_id`     | Required. The company the plan is for.                                                                                                   |
| `template_id`    | Required. An id from [the templates](#templates).                                                                                        |
| `contact_ids`    | The company's contacts the plan is with. Left out, the plan is with the company's first current contact. An empty list means no one yet. |
| `key_contact_id` | The key contact. The first of `contact_ids`, unless you name one.                                                                        |
| `owner_id`       | The plan's owner. The token's user, unless you name someone.                                                                             |
| `team_ids`       | Other team members on the plan.                                                                                                          |
| `start_date`     | The day the steps count from. Today, unless you send another.                                                                            |
| `name`           | The template's name, unless you send another.                                                                                            |
| `goal`           | What the plan is for.                                                                                                                    |

```bash
curl -X POST "https://api.tiny.plus/v2/relationship_plans" \
  -H "Authorization: Bearer {{user_access_token}}" \
  -H "Content-Type: application/json" \
  -d '{
        "company_id": 8813,
        "template_id": 4,
        "contact_ids": [62377],
        "goal": "win a new project with them"
      }'
```

The answer is the new plan, as [Get one plan](#get-one-plan) returns it. Its first follow-up is already booked, as a task for the owner.

An owner or team member other than the token's user is sent a notification that they're on the plan.

## Log a follow-up

<mark style="color:green;">`POST`</mark> `https://api.tiny.plus/v2/relationship_plans/{{id}}/touches`

| Field         | Used with           | Description                                                                                                                                    |
| ------------- | ------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
| `outcome`     | all                 | Required: `done`, `no_answer`, `skipped` or `moved`.                                                                                           |
| `task_id`     | all                 | Which follow-up, from the plan's `followups`. It can be left out when exactly one is open. With several open it answers `400`, and lists them. |
| `touch_type`  | `done`, `no_answer` | How it was done. The follow-up's own type, unless you send another. See [Touch types](#touch-types).                                           |
| `touch_date`  | `done`, `no_answer` | The day it happened: today or earlier. Today, unless you send another.                                                                         |
| `contact_ids` | `done`              | Who it was with, from the plan's `contacts`. The follow-up's own `contact_ids`, unless you send others.                                        |
| `notes`       | `done`              | What was said. Posted on the company's news feed and on those contacts'.                                                                       |
| `main_feed`   | `done`              | `true` to show the notes on the main news feed as well.                                                                                        |
| `next_date`   | `done`              | A day for the next follow-up, today or later, when it shouldn't follow the plan's schedule.                                                    |
| `new_date`    | `moved`             | Required for `moved`: the day the follow-up moves to, today or later.                                                                          |

```bash
curl -X POST "https://api.tiny.plus/v2/relationship_plans/{{id}}/touches" \
  -H "Authorization: Bearer {{user_access_token}}" \
  -H "Content-Type: application/json" \
  -d '{
        "outcome": "done",
        "touch_type": "call",
        "notes": "Spoke to Susan today. She would like our aged care case studies."
      }'
```

```json
{
    "logged": { "task_id": 155150, "outcome": "done" },
    "plan": { "id": 155147, "...": "..." }
}
```

`plan` is the whole plan as it now stands, with the follow-up it booked next.

| Outcome     | What it does                                                                                                              |
| ----------- | ------------------------------------------------------------------------------------------------------------------------- |
| `done`      | Completes the task, posts the notes if there are any, and books the next follow-up, which is the one `if_done` described. |
| `no_answer` | Moves the same follow-up a few working days on: three, unless the account has set another number.                         |
| `skipped`   | Drops this follow-up and books the one after it.                                                                          |
| `moved`     | Moves the follow-up to `new_date`.                                                                                        |

Before offering a main feed choice, read the plan's `main_feed`:

* `null`: notes can't go on the main news feed, because the company is confidential or the account keeps plan notes off it. `main_feed` in your request is ignored.
* `true` or `false`: the choice is allowed, and this is how tiny+ itself starts it.

Completing a follow-up's task through [`/v2/activities`](/api/endpoints/tasks.md) also counts as done, without notes, and books the next one.

### Touch types

`any`, `call`, `email`, `meeting`, `coffee` (coffee or lunch), `visit` (a site visit), `event`, `send` (send something) and `linkedin`.

### Counting contact logged elsewhere

Someone might meet one of a plan's contacts, log an email with one, or post about the company. A plan read then carries an `offer`, so the person can count that contact as the follow-up without logging it twice:

```json
{
    "offer": {
        "source": {
            "type": "meeting",
            "id": 160661,
            "title": "Budget review",
            "date": "2026-09-26",
            "by": { "id": 187, "name": "Laurence Persimmon" }
        },
        "contacts": [
            { "id": 62377, "name": "Susan Aitchinson" }
        ],
        "task_id": 159220,
        "touch_type": "meeting",
        "if_counted": { "title": "Invite to an event", "due_date": "2026-10-26" }
    }
}
```

| Field        | Meaning                                                                                                                               |
| ------------ | ------------------------------------------------------------------------------------------------------------------------------------- |
| `source`     | What happened: its `type` is `meeting`, `email` or `post`, and `title` is the meeting's name, the email's subject or the post's text. |
| `contacts`   | The plan's contacts it was with. Empty when it was only about the company.                                                            |
| `task_id`    | The open follow-up it would count for.                                                                                                |
| `if_counted` | What the plan would book next.                                                                                                        |

`offer` is `null` when there is nothing to offer, and always for someone who can't change the plan. Only contact since the plan's last one is offered.

* **To count it,** send `{"source_id": <source.id>}` to `POST …/touches`. The follow-up is logged as done on `source.date`, with those contacts, by `source.by`. Nothing is posted, because it's on the news feed already. `task_id` is the offer's unless you send another, and `outcome` can be left out.
* **To turn it down,** send `{"dismiss_source_id": <source.id>}` to `PATCH /v2/relationship_plans/{id}`. It isn't offered again, and nor is anything older.

Either answers `409` once it is no longer offered. A touch counted this way has a `source` with the same `type`, `id` and `title`.

## Change a plan

<mark style="color:purple;">`PATCH`</mark> `https://api.tiny.plus/v2/relationship_plans/{{id}}`

Send any of:

| Field                           | Description                                                                                       |
| ------------------------------- | ------------------------------------------------------------------------------------------------- |
| `name`                          | The plan's name.                                                                                  |
| `owner_id`                      | A new owner. The open follow-ups go with the plan.                                                |
| `team_ids`                      | The whole team, replacing the one it has.                                                         |
| `contact_ids`, `key_contact_id` | The contacts the plan is with, replacing the ones it has, and which of them is the key contact.   |
| `state`                         | `paused`, `active` or `stopped`.                                                                  |
| `until`                         | With `paused`: the day the plan starts again. Left out, it stays paused until someone resumes it. |
| `reason`                        | With `stopped`: `won_work`, `went_quiet`, `contact_left`, `not_a_fit` or `other`.                 |

```bash
curl -X PATCH "https://api.tiny.plus/v2/relationship_plans/{{id}}" \
  -H "Authorization: Bearer {{user_access_token}}" \
  -H "Content-Type: application/json" \
  -d '{"state": "paused", "until": "2026-11-02"}'
```

The answer is the plan as it now stands. Pausing or stopping a plan removes its open follow-ups, and setting a paused plan to `active` books them again. A stopped or finished plan can't be changed.

A state the plan can't reach answers `409`, and nothing else in the request is saved. A plan's steps can't be changed through the API.

## Templates

<mark style="color:blue;">`GET`</mark> `https://api.tiny.plus/v2/relationship_plan_templates`

The templates the token's user can start a plan from: the account's own, then any they keep for themselves.

```bash
curl "https://api.tiny.plus/v2/relationship_plan_templates" \
  -H "Authorization: Bearer {{user_access_token}}"
```

```json
{
    "total_records": 6,
    "records": [
        {
            "id": 1,
            "name": "Monthly catch-up",
            "description": "Stay in touch with a current client.",
            "visibility": "firm",
            "standard": true,
            "key": "monthly_catch_up",
            "summary": "Every month, first in 2 weeks",
            "schedule_from": "done",
            "steps": [
                {
                    "title": "Catch-up",
                    "touch_type": "any",
                    "anchor": "start",
                    "offset": [2, "week"],
                    "repeat": [1, "month"],
                    "times": null,
                    "who": "owner",
                    "with": "key",
                    "notes": ""
                }
            ]
        }
    ]
}
```

`visibility` is `firm` for a template everyone can use, or `personal` for one of the token user's own. `standard` marks the six that come with tiny+. `key` is what a plan's `template` holds.

## Errors

| Status | When                                                                                                                                                                                                                                          |
| ------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `400`  | Something in the request is wrong: `{"error": "outcome is done, no_answer, skipped or moved."}`. Also a log with no `task_id` when several follow-ups are open: the message lists them.                                                       |
| `403`  | The token's user may not do it: `{"error": "Only the plan's owner, its team or a records admin can change it."}`. Also the account has no plans, and a [read-only token](/api/concepts/permissions.md#read-only-tokens) trying to change one. |
| `404`  | There is no such plan, or the token's user can't see its company: `{"error": "That plan could not be found."}`                                                                                                                                |
| `409`  | The plan's state rules it out: `{"error": "Only a paused plan can be resumed."}`. Also a follow-up that has already been dealt with, a plan with none open, and an offer that has gone.                                                       |
