> 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/account-structure.md).

# Account structure

Every tiny+ account is set up differently: it renames fields, adds custom fields and tag collections, and chooses its own statuses and phases. These endpoints describe how one account is arranged, so your integration can adapt to it instead of assuming.

They change rarely, so read them once and keep a copy rather than calling them before every request. All are `GET`, and any token on the account may call them. [Record types and your account's fields](/api/concepts/record-types.md) explains how to *use* what they return when you read and write records.

## Fields

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

The standard fields of a record type, keyed by field name. Type names are case-sensitive (`projects`, not `Projects`).

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

```json
{
    "name": {
        "field_name": "name",
        "field_type": "name_with_search",
        "field_description": "Project Name",
        "field_allowedit": "1",
        "val_required": "1"
    },
    "record_status": {
        "field_name": "record_status",
        "field_type": "fancy_select",
        "field_description": "Project Status",
        "field_allowedit": "1",
        "options": [
            { "value": "proposal", "label": "Proposed", "colour": "#62c989" },
            { "value": "won", "label": "Won!", "colour": "#1cb5e8" }
        ]
    },
    "primary_company_id": {
        "field_name": "primary_company_id",
        "field_type": "id",
        "field_description": "Client Company",
        "field_allowedit": "1",
        "allowed_flavours": ["companies"]
    }
}
```

Each field carries its `field_name`, `field_type`, `field_description` (the account's label), `field_allowedit` (`1` if you can write it) and `val_required` (`1` if it's required). Two more appear where they apply:

* **`options`** on `select` and `fancy_select` fields: the values you may send, each with a `value`, `label` and `colour`. A `timezone` field has them too, without a `colour`: the time zones tiny+ offers, such as `{ "value": "Australia/Sydney", "label": "(UTC+10:00) Sydney, Canberra" }`. See [Time zone](/api/endpoints/team-members.md#time-zone).
* **`allowed_flavours`** on `id` fields: the record types the field may point to, such as `["companies"]`.

{% hint style="warning" %}
This lists **standard fields only** — no custom fields or tag collections. Those come from [Forms](#forms). It also lists internal fields such as `id` and `search_fields`, which you can't write.

On accounts that split statuses into phases, `record_status`'s `options` are the statuses only. They aren't the values a write accepts — for that, use [Statuses and phases](#statuses-and-phases) below.
{% endhint %}

## Forms

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

A form the account has arranged, as groups of fields in the account's own order. `{{form}}` is `view`, `edit` or `add`, and defaults to `view`. This is the only endpoint that lists an account's **custom fields, tag collections, team-role fields and relationship fields**, so it's the place to discover them.

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

The response has the form's details and a `groups` array. Each group has a heading and an `items` array, and each item carries `field_info` — the field's definition, with the same keys as [Fields](#fields) above. Identify a field by its `field_info.field_name`:

* A **custom field** is `custom_{id}`, for example `custom_6903`. Its `field_type` is the custom field's type (`char`, `int`, `date`, `bit`, `currency`, `percentage` or `file`).
* A **tag collection** is `tags_{id}`, with `field_type` `tagcollection`. Its item also carries a `tags` array, each tag having an `id`, `name` and `colour`.
* A **team-role field** is `teams_{id}`, and a **relationship field** is `extra_related_{id}`.

Fields that tiny+ fills in itself are left out of the `add` and `edit` forms, since there's nothing to send for them.

{% hint style="info" %}
Any name after the type that isn't `view`, `edit` or `add` is treated as a named form. A name that no form matches returns the view form (not an error), so check the `form_name` in the response is the one you meant. Types with no form at all — such as currencies — return `404 {"error": "Could not find form."}`.
{% endhint %}

## Statuses and phases

Two endpoints describe an account's statuses. Use `record_status` to know what a write accepts, and `status_schema` to lay them out.

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

The values `record_status` accepts on a write, in order. For a status split into phases, you get one entry per phase, and the bare status isn't listed; otherwise one entry per status. The `id` is what you send — a phase id, or a status value.

```json
[
    { "id": "195", "key": "proposal|195", "label": "Proposed - Long Term" },
    { "id": "won", "key": "won|0", "label": "Won!" },
    { "id": "62133", "key": "active|62133", "label": "Active - Act" }
]
```

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

Every status with its phases as structured data — labels, colours, and which status is the default:

```json
{
    "flavour": "projects",
    "statuses": [
        {
            "status": "proposal",
            "label": "Proposed",
            "colour": "#62c989",
            "default": true,
            "phases": [
                { "id": 195, "label": "Long Term", "colour": "#62c989" }
            ]
        },
        { "status": "won", "label": "Won!", "colour": "#1cb5e8", "default": false, "phases": [] }
    ]
}
```

The `status` values (`proposal`, `won`, …) are fixed and safe to key on; the `label` is the account's name for it and can change. There are no won/lost categories — key on the `status`. [Statuses and phases](/api/concepts/record-types.md#statuses-and-phases) covers sending them on a write.

A type with no statuses — such as emails — returns `400 {"error": "Could not load statuses."}`.

## Team members

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

A short list of every team member, for populating an assignee picker. Sorted by name, and including active, deactivated and ghost members (but not deleted ones).

```json
[
    {
        "id": "191",
        "name": "Maya Lin",
        "role": "Marketing Manager",
        "email": "maya@example.com",
        "record_url": "https://crm.tiny.plus/staff/191",
        "hero_path_uri": "https://crm.tiny.plus/img/user.gif"
    }
]
```

There's no status here and no paging — every member comes back. To filter by status, or get the full team member records, use [`GET /v2/staff`](/api/endpoints/team-members.md).

## Buttons

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

The account's custom button fields, across every record type. Buttons are how an account triggers a [webhook](/api/endpoints/webhooks.md).

```json
[
    {
        "id": "11187",
        "name": "Create a Fee Proposal",
        "nice_name": "Create a Fee Proposal (Projects)",
        "flavour": "projects"
    }
]
```

`id` is the button's custom field id, and `flavour` is the record type it sits on. An account with no buttons gets `[]`.

## Errors

| Case                                       | Response                                                                                       |
| ------------------------------------------ | ---------------------------------------------------------------------------------------------- |
| No type given                              | `400 {"error": "Must specify a flavour."}`                                                     |
| Unknown type, or a type without API access | `404 {"error": "No Endpoint:{type}"}`                                                          |
| Unknown form name                          | `200` with the view form; `404 {"error": "Could not find form."}` only for a type with no form |
| A type with no statuses                    | `400 {"error": "Could not load statuses."}`                                                    |
| Missing or invalid token                   | `401`, in the [OAuth error shape](/api/concepts/requests.md#errors)                            |
