# Welcome!

## Welcome to the Frill Developer Docs

Here you'll find all the documentation you need to get up and running with the Frill API.

## Want to jump right in?

Feeling like an eager beaver? Jump in to the quick start docs and get making your first request:

{% content-ref url="/pages/nKxTnHz3KZiLtv1ifWjJ" %}
[Quick start](/api/quick-start)
{% endcontent-ref %}

## Want to deep dive?

Dive a little deeper and start exploring our API reference to get an idea of everything that's possible with the API:

{% content-ref url="/pages/4wqViHXhfOTPCsJHiPAp" %}
[Reference](/api/reference)
{% endcontent-ref %}


# Quick start

{% hint style="info" %}
**Good to know:** A quick start guide can be good to help folks get up and running with your API in a few steps. Some people prefer diving in with the basics rather than meticulously reading every page of documentation!
{% endhint %}

## Get your API Key

Your API requests are authenticated using an API Key. Any request that doesn't include an API Key will return an error.

You can request an API Key from the [Frill Dashboard](https://app.frill.co/settings/company) at any time.

{% embed url="<https://app.frill.co/settings/company>" %}

For more details on Authenticating API requests with an API Key jump into the Authentication section.

{% content-ref url="/pages/WxtcnfsNuMsZZOBE75CQ" %}
[Authentication](/api/authentication)
{% endcontent-ref %}

## Make your first request

To make your first request, send an authenticated request to the Ideas endpoint. This will return a list of your current `Ideas`.

{% openapi src="/files/D1SzF4FpVvlKOgJs0R3j" path="/ideas" method="get" %}
[Broken mention](broken://files/D1SzF4FpVvlKOgJs0R3j)
{% endopenapi %}

Take a look at how you might call this method using `curl`:

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

```
curl https://api.frill.co/v1/ideas  
    -u YOUR_API_KEY:    
```

{% endtab %}
{% endtabs %}


# Authentication

Each API request requires an API key either as a HTTP Header or included as a query string.

You can find your API key in the Frill Dashboard.

{% hint style="warning" %}
Your API key has many privileges, be sure to keep it secret! Do not share the key in publicly accessible areas such GitHub, client-side code, and so forth.
{% endhint %}

{% hint style="info" %}
**Recommendation:** It's best to use the *HTTP Header - Bearer auth* method - it's the easier than the *Basic auth* method and more secure than *Query string*
{% endhint %}

### **HTTP Header - Bearer auth**

Include the API Key in the `Authorization` header where the value is prefixed with `Bearer` in the format:

```http
GET https://api.frill.co/v1/ideas
Authorization: Bearer API_KEY
```

### **Query string**

Include the API key as part of the request path in the format `?api_key=API_KEY`.

```http
GET https://api.frill.co/v1/ideas?api_key=API_KEY
```

### **HTTP Header - Basic auth**

Following the [Authorization Basic HTTP Spec](https://en.wikipedia.org/wiki/Basic_access_authentication#Client_side). Provide your API key as the basic auth **username** value. You do not need to provide a password.

The Authorization field is constructed as follows:

1. The username (api\_key) and password (leave blank) are combined with a single colon. (:)
2. The resulting string is encoded into an octet sequence.
3. The resulting string is encoded using Base64.
4. The authorization method and a space is then prepended to the encoded string, separated with a space (e.g. "Basic ").

```http
GET https://api.frill.co/v1/ideas
Authorization: Basic QWxhZGRpbjpPcGVuU2VzYW1l
```

***


# Responses

### Content type

Make sure the following headers are set correctly in order to return a JSON response.

* Set `Content-Type` header to `application/json`
* Set `Accept` header to `application/json`

### Successful response

Successful API queries will return a `200` HTTP Response code.

The reesponse body will be in the following format, there will always be `data` and `meta` fields and for list of items there will be a `pagination` field:

```json
{
  "data": {
    // data 
  },
  "meta": {
    // any meta data we need to send eg rate limits etc
  },

  // Optional (included if the data contains a list of items)
  "pagination": {
    // pagination data
  },
}
```

For single object (eg an Announcement) `data` will be an object:

```json
{
  "data": {
    "idx": "announcement_abcd123",
    "name": "My first announcement",
    "slug": "my-first-announcement",
    // .. etc
  },
  "meta": {
    // any meta data we need to send eg rate limits etc
  }
}
```

For multiple objects (eg a list of Announcements) `data` will be an array of objects:

```json
{
  "data": [
    {
      "idx": "announcement_abcd123",
      "name": "My first announcement",
      "slug": "my-first-announcement",
     // .. etc
    },
    {
      "idx": "announcement_abcd098",
      "name": "My second announcement",
      "slug": "my-second-announcement",
     // .. etc
    }
  ],
  "pagination": {
    "total": 100,
    // any pagination data we need to send
  }
  "meta": {
    // any meta data we need to send eg rate limits etc
  }
}
```

### Error response

As much as possible, Frill attempts to use appropriate HTTP status codes to indicate the general class of problem, and this status code is repeated in the code section of the meta response.

**400 (Bad Request)**\
Any case where a parameter is invalid, or a required parameter is missing. This includes the case where no OAuth token is provided and the case where a resource ID is specified incorrectly in a path.

**403 (Forbidden)**\
The requested information cannot be viewed by the acting user, for example, because they are not friends with the user whose data they are trying to read.

**404 (Not Found)**\
Endpoint does not exist.

**405 (Method Not Allowed)**\
Attempting to use POST with a GET-only endpoint, or vice-versa.

**422 (Unprocessable Entity)**\
The request could not be completed as it is. Use the information included in the response to modify the request and retry.

**500 (Internal Server Error)**\
Frill's servers are unhappy. The request is probably valid but needs to be retried later. If the problem persist please contact <support@frill.co>

Error responses will return JSON a message in the following format.

```json
{
  "error": true,
  "message": "Please provide a status_id"
}
```


# Pagination

List of items returned by the API are paginated using cursor based pagination. Cursor-based pagination works by returning an alphanumeric pointer to a specific item in the list - for the Frill API this is the last item in the list.

Subsequent requests to the API can be passed this alphanumeric pointer to return the next set of results after the given pointer.

An API response that contains a list of items will contain a `pagination` object (see example below) containing information about the list of items, such as:- the `count` of items return, the `total` number of items and the `endCursor` which can be used to get the next set of results.

The cursor is returned in the `pagination.endCursor` field should be passed to the next request as an `after` parameter - eg:

`/api/ideas?after=END_CURSOR_FROM_PREVIOUS_REQUEST`

#### Pagination request params

| Name    | Description                                                 | Default |
| ------- | ----------------------------------------------------------- | ------- |
| `after` | Provide a cursor to get the results after the pointer/item  |         |
| `limit` | The number of items to return. Can't be be greater than 100 | 20      |

#### Pagination result fields

| Name          | Description                                                                                                               |
| ------------- | ------------------------------------------------------------------------------------------------------------------------- |
| `total`       | Total number of available items                                                                                           |
| `count`       | Number of items in current response                                                                                       |
| `hasNextPage` | Is another page of items available                                                                                        |
| `startCursor` | Cursor for the start of the results                                                                                       |
| `endCursor`   | Cursor for the end of the results. Should be passed as the `after` param to subsequent request to get next set of results |

See example below

```json
{
  "data": [...],
  "pagination": {
    "total ": 20,
    "count ": 2,
    "hasNextPage ": true,
    "startCursor ": "CURSOR",
    "endCursor ": "CURSOR"
  },
}
```


# Reference

Dive into the specifics of each API endpoint by checking out our complete documentation.

## Ideas

All the methods managing ideas in Frill

{% content-ref url="/pages/CVjkVWfipLLMMmPlfvIs" %}
[Ideas](/api/reference/ideas)
{% endcontent-ref %}

## Announcements

Everything related to announcements in Frill

{% content-ref url="/pages/eiHhkD6MNJ9SMcrugGBh" %}
[Announcements](/api/reference/announcements)
{% endcontent-ref %}

## Announcement Categories

Manage announcement categories for organizing your announcements

{% content-ref url="/pages/0pO4lqPdUtjq5kfovezV" %}
[Announcement Categories](/api/reference/announcement-categories)
{% endcontent-ref %}

## Comments

All the methods for managing comments on ideas

{% content-ref url="/pages/QPkvPkxqVQFyZUxQCfhb" %}
[Comments](/api/reference/comments)
{% endcontent-ref %}

## Followers

Everything related to follower management in Frill

{% content-ref url="/pages/Fn3UAemxu30BkAQvkmFp" %}
[Followers](/api/reference/followers)
{% endcontent-ref %}

## Statuses

All the methods for managing idea statuses

{% content-ref url="/pages/TGLiGMsFnMQv7YqeOskR" %}
[Statuses](/api/reference/statuses)
{% endcontent-ref %}

## Topics

Everything related to topic management in Frill

{% content-ref url="/pages/ZiF1V92DR67GZdX3LHJ3" %}
[Topics](/api/reference/topics)
{% endcontent-ref %}

## Votes

All the methods for managing votes on ideas

{% content-ref url="/pages/M82MfxmWnrQhWzxufNNT" %}
[Votes](/api/reference/votes)
{% endcontent-ref %}

## Integration Links

Read the external records — such as Zendesk tickets — linked to your ideas

{% content-ref url="/pages/IXJaXbnWuyInnqF31y6Y" %}
[Integration Links](/api/reference/integration-links)
{% endcontent-ref %}


# Announcements

Announcements provide updates on product releases, completed Ideas feature enhancements, and any changes made to your  product. These posts serve as a detailed change-log, keeping users

## Announcement object

Announcements have the following fields

| Field                                             | Details                                                                                                              |
| ------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------- |
| <p><strong>idx</strong><br>string</p>             | A unique identifier for the announcement                                                                             |
| <p><strong>name</strong><br>string</p>            | Name of the announcement                                                                                             |
| <p><strong>slug</strong><br>string</p>            | The URL slug of the announcement                                                                                     |
| <p><strong>excerpt</strong><br>string</p>         | Short textual description of the announcement                                                                        |
| <p><strong>content</strong><br>string</p>         | Content of the announcement (as Markdown)                                                                            |
| <p><strong>content\_html</strong><br>string</p>   | Content of the announcement (as HTML)                                                                                |
| <p><strong>reaction\_count</strong><br>object</p> | Reactions to the announcement                                                                                        |
| <p><strong>is\_published</strong><br>boolean</p>  | Indicates if the announcement is published or not                                                                    |
| <p><strong>published\_at</strong><br>string</p>   | The date & time the announcement was published (UTC timezone) or null if still in draft                              |
| <p><strong>created\_at</strong><br>string</p>     | The date & time the announcement was created (UTC timezone)                                                          |
| <p><strong>updated\_at</strong><br>string</p>     | The date & time the announcement was last updated (UTC timezone)                                                     |
| <p><strong>author</strong><br>object</p>          | Author of this announcement                                                                                          |
| <p><strong>categories</strong><br>array</p>       | Categories associated with this announcement (see [Announcement Categories](/api/reference/announcement-categories)) |
| <p><strong>ideas</strong><br>array</p>            | Ideas associated with this announcement                                                                              |

### Endpoints

You can use the following endpoints to manage Announcements

{% openapi src="/files/3IeSmtlpFc9gA6fdUfCf" path="/announcements" method="get" %}
[announcements.yaml](https://554427104-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FIpllvC2FiBlBNS7dyV5t%2Fuploads%2Fgit-blob-9024ffb00fe407223ecddfe3e27bf3f8da05cb0c%2Fannouncements.yaml?alt=media)
{% endopenapi %}

{% openapi src="/files/3IeSmtlpFc9gA6fdUfCf" path="/announcements" method="post" %}
[announcements.yaml](https://554427104-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FIpllvC2FiBlBNS7dyV5t%2Fuploads%2Fgit-blob-9024ffb00fe407223ecddfe3e27bf3f8da05cb0c%2Fannouncements.yaml?alt=media)
{% endopenapi %}

{% openapi src="/files/3IeSmtlpFc9gA6fdUfCf" path="/announcements/{announcementIdx}" method="get" %}
[announcements.yaml](https://554427104-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FIpllvC2FiBlBNS7dyV5t%2Fuploads%2Fgit-blob-9024ffb00fe407223ecddfe3e27bf3f8da05cb0c%2Fannouncements.yaml?alt=media)
{% endopenapi %}

{% openapi src="/files/3IeSmtlpFc9gA6fdUfCf" path="/announcements/{announcementIdx}" method="post" %}
[announcements.yaml](https://554427104-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FIpllvC2FiBlBNS7dyV5t%2Fuploads%2Fgit-blob-9024ffb00fe407223ecddfe3e27bf3f8da05cb0c%2Fannouncements.yaml?alt=media)
{% endopenapi %}

{% openapi src="/files/3IeSmtlpFc9gA6fdUfCf" path="/announcements/{announcementIdx}" method="delete" %}
[announcements.yaml](https://554427104-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FIpllvC2FiBlBNS7dyV5t%2Fuploads%2Fgit-blob-9024ffb00fe407223ecddfe3e27bf3f8da05cb0c%2Fannouncements.yaml?alt=media)
{% endopenapi %}


# Announcement Categories

Announcement Categories help organize your announcements into logical groups, making it easier for users to find relevant updates. Categories can be customized with names and colors to match your prod

## Category object

Announcement Categories have the following fields

| Field                                          | Details                                                      |
| ---------------------------------------------- | ------------------------------------------------------------ |
| <p><strong>id</strong><br>integer</p>          | Category ID                                                  |
| <p><strong>idx</strong><br>string</p>          | Encoded category ID                                          |
| <p><strong>name</strong><br>string</p>         | Category name                                                |
| <p><strong>color</strong><br>string</p>        | Hex RGB color code                                           |
| <p><strong>company\_id</strong><br>integer</p> | Company ID                                                   |
| <p><strong>created\_at</strong><br>string</p>  | The date & time the category was created (UTC timezone)      |
| <p><strong>updated\_at</strong><br>string</p>  | The date & time the category was last updated (UTC timezone) |
| <p><strong>translations</strong><br>object</p> | Localized category names                                     |

### Endpoints

You can use the following endpoints to manage Announcement Categories

{% openapi src="/files/yZwHZ70AHTRRVb1v2VPA" path="/api/v1/announcement-categories" method="get" %}
[announcement\_categories.yaml](https://554427104-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FIpllvC2FiBlBNS7dyV5t%2Fuploads%2Fgit-blob-5b1dbb533a7af50ef64e6b35f55b784ae400d605%2Fannouncement_categories.yaml?alt=media)
{% endopenapi %}

{% openapi src="/files/yZwHZ70AHTRRVb1v2VPA" path="/api/v1/announcement-categories" method="post" %}
[announcement\_categories.yaml](https://554427104-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FIpllvC2FiBlBNS7dyV5t%2Fuploads%2Fgit-blob-5b1dbb533a7af50ef64e6b35f55b784ae400d605%2Fannouncement_categories.yaml?alt=media)
{% endopenapi %}

{% openapi src="/files/yZwHZ70AHTRRVb1v2VPA" path="/api/v1/announcement-categories/{categoryIdx}" method="get" %}
[announcement\_categories.yaml](https://554427104-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FIpllvC2FiBlBNS7dyV5t%2Fuploads%2Fgit-blob-5b1dbb533a7af50ef64e6b35f55b784ae400d605%2Fannouncement_categories.yaml?alt=media)
{% endopenapi %}

{% openapi src="/files/yZwHZ70AHTRRVb1v2VPA" path="/api/v1/announcement-categories/{categoryIdx}" method="post" %}
[announcement\_categories.yaml](https://554427104-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FIpllvC2FiBlBNS7dyV5t%2Fuploads%2Fgit-blob-5b1dbb533a7af50ef64e6b35f55b784ae400d605%2Fannouncement_categories.yaml?alt=media)
{% endopenapi %}

{% openapi src="/files/yZwHZ70AHTRRVb1v2VPA" path="/api/v1/announcement-categories/{categoryIdx}" method="delete" %}
[announcement\_categories.yaml](https://554427104-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FIpllvC2FiBlBNS7dyV5t%2Fuploads%2Fgit-blob-5b1dbb533a7af50ef64e6b35f55b784ae400d605%2Fannouncement_categories.yaml?alt=media)
{% endopenapi %}

## Related

* [Announcements](/api/reference/announcements) - Learn how to manage announcements that use these categories


# Ideas

Ideas are feedback or suggestions submitted by customers to your Frill account.  Each idea can be assigned a Status and categorized under multiple Topics.

## Idea object

Ideas have the following fields

| Field                                                         | Details                                                                                                                           |
| ------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------- |
| <p><strong>idx</strong><br>string</p>                         | A unique identifier for the idea                                                                                                  |
| <p><strong>name</strong><br>string</p>                        | The name of the idea                                                                                                              |
| <p><strong>slug</strong><br>string</p>                        | The URL slug of the idea                                                                                                          |
| <p><strong>url</strong><br>string</p>                         | Direct link to the idea on the public board                                                                                       |
| <p><strong>excerpt</strong><br>string</p>                     | A short excerpt or summary of the idea                                                                                            |
| <p><strong>description</strong><br>string</p>                 | The text description of the idea (legacy field, use content fields for structured data)                                           |
| <p><strong>content</strong><br>array</p>                      | Structured content as a JSON array (similar to Slate.js/ProseMirror format)                                                       |
| <p><strong>content\_html</strong><br>string</p>               | Content rendered as HTML                                                                                                          |
| <p><strong>content\_markdown</strong><br>string</p>           | Content rendered as Markdown                                                                                                      |
| <p><strong>cover\_image</strong><br>string</p>                | Url of the image displayed as the cover on the Roadmap view                                                                       |
| <p><strong>vote\_count</strong><br>number</p>                 | Number of votes this idea has received                                                                                            |
| <p><strong>comment\_count</strong><br>number</p>              | Number of comments on this idea                                                                                                   |
| <p><strong>note\_count</strong><br>number</p>                 | Number of internal notes on this idea                                                                                             |
| <p><strong>is\_pinned</strong><br>boolean</p>                 | Has the idea been pinned to the top                                                                                               |
| <p><strong>is\_bug</strong><br>boolean</p>                    | Denotes if this idea is a bug                                                                                                     |
| <p><strong>is\_archived</strong><br>boolean</p>               | Denotes if this idea has been archived                                                                                            |
| <p><strong>is\_completed</strong><br>boolean</p>              | Denotes if this idea is considered completed / finished                                                                           |
| <p><strong>show\_in\_roadmap</strong><br>boolean</p>          | Should the idea be displayed in the Roadmap view                                                                                  |
| <p><strong>approval\_status</strong><br>string</p>            | The approval status of the idea: `approved\|needs-approval\|rejected`                                                             |
| <p><strong>priority\_score</strong><br>number</p>             | The computed composite prioritization score (`0` when the idea has not been prioritized — see `is_prioritized`)                   |
| <p><strong>priority\_score\_normalized</strong><br>number</p> | The prioritization score normalized to a 0-100 scale (relative to the company's maximum possible benefit score)                   |
| <p><strong>priority\_score\_benefits</strong><br>number</p>   | The benefit component of the prioritization score                                                                                 |
| <p><strong>priority\_score\_costs</strong><br>number</p>      | The cost component of the prioritization score                                                                                    |
| <p><strong>is\_prioritized</strong><br>boolean</p>            | Whether every required prioritization factor has been scored for this idea                                                        |
| <p><strong>priorities</strong><br>object</p>                  | The idea's raw 0-10 score for each prioritization factor, keyed by the factor's `company_priority` idx (see Prioritization below) |
| <p><strong>created\_at</strong><br>string</p>                 | The date & time the idea was created in UTC timezone                                                                              |
| <p><strong>updated\_at</strong><br>string</p>                 | The date & time the idea was last updated (UTC timezone)                                                                          |
| <p><strong>author</strong><br>object</p>                      | Author of the idea                                                                                                                |
| <p><strong>status</strong><br>object</p>                      | Status of the idea                                                                                                                |
| <p><strong>topics</strong><br>array</p>                       | Topics associated with the idea                                                                                                   |
| <p><strong>attachments</strong><br>array</p>                  | Files and images attached to this idea                                                                                            |

## Prioritization

Ideas carry prioritization data so you can pull priority scores into your own tools instead of only sorting by votes.

* Each idea returns a composite `priority_score` (plus `priority_score_normalized`, `priority_score_benefits` and `priority_score_costs`), and a `priorities` map of the raw 0-10 score for each factor, keyed by that factor's `company_priority` idx.
* The factor definitions (name, benefit/cost, weight) are returned **once per response** in `meta.company_priorities` — use each factor's `idx` to interpret the per-idea `priorities` map. The `votes` factor, when configured, is derived from the idea's votes and included in the map.
* `is_prioritized` is `true` only when every required factor has been scored. Unprioritized ideas report a `priority_score` of `0`.
* On `GET /ideas` you can sort by `priority_score` (`sortBy=priority_score`) and filter by prioritization state with `is_prioritized` (`true` for fully-prioritized ideas, `false` for those still needing prioritization).

## Endpoints

You can use the following endpoints to manage Ideas

{% openapi src="/files/nvWLOcVQy6aMzLlDG4cO" path="/ideas" method="get" %}
[ideas.yaml](https://554427104-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FIpllvC2FiBlBNS7dyV5t%2Fuploads%2Fgit-blob-552db8b6245713dd61480fa22058c96f03b58888%2Fideas.yaml?alt=media)
{% endopenapi %}

{% openapi src="/files/nvWLOcVQy6aMzLlDG4cO" path="/ideas/search" method="get" %}
[ideas.yaml](https://554427104-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FIpllvC2FiBlBNS7dyV5t%2Fuploads%2Fgit-blob-552db8b6245713dd61480fa22058c96f03b58888%2Fideas.yaml?alt=media)
{% endopenapi %}

{% openapi src="/files/nvWLOcVQy6aMzLlDG4cO" path="/ideas" method="post" %}
[ideas.yaml](https://554427104-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FIpllvC2FiBlBNS7dyV5t%2Fuploads%2Fgit-blob-552db8b6245713dd61480fa22058c96f03b58888%2Fideas.yaml?alt=media)
{% endopenapi %}

{% openapi src="/files/nvWLOcVQy6aMzLlDG4cO" path="/ideas/{ideaIdxOrSlug}" method="get" %}
[ideas.yaml](https://554427104-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FIpllvC2FiBlBNS7dyV5t%2Fuploads%2Fgit-blob-552db8b6245713dd61480fa22058c96f03b58888%2Fideas.yaml?alt=media)
{% endopenapi %}

{% openapi src="/files/nvWLOcVQy6aMzLlDG4cO" path="/ideas/{ideaIdx}" method="post" %}
[ideas.yaml](https://554427104-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FIpllvC2FiBlBNS7dyV5t%2Fuploads%2Fgit-blob-552db8b6245713dd61480fa22058c96f03b58888%2Fideas.yaml?alt=media)
{% endopenapi %}

{% openapi src="/files/nvWLOcVQy6aMzLlDG4cO" path="/ideas/{ideaIdx}" method="delete" %}
[ideas.yaml](https://554427104-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FIpllvC2FiBlBNS7dyV5t%2Fuploads%2Fgit-blob-552db8b6245713dd61480fa22058c96f03b58888%2Fideas.yaml?alt=media)
{% endopenapi %}


# Comments

Comments allow users to provide feedback, ask questions, and engage in discussions about specific Ideas. These comments serve as a way for users to collaborate and share thoughts on product features a

## Comment object

Comments have the following fields

| Field                                               | Details                                                                     |
| --------------------------------------------------- | --------------------------------------------------------------------------- |
| <p><strong>idx</strong><br>string</p>               | A unique identifier for the comment                                         |
| <p><strong>message</strong><br>string</p>           | The raw message content of the comment                                      |
| <p><strong>content</strong><br>array</p>            | Structured content as a JSON array (similar to Slate.js/ProseMirror format) |
| <p><strong>content\_html</strong><br>string</p>     | Content rendered as HTML                                                    |
| <p><strong>content\_markdown</strong><br>string</p> | Content rendered as Markdown                                                |
| <p><strong>is\_private</strong><br>boolean</p>      | Indicates if the comment is private (note) or public (comment)              |
| <p><strong>type</strong><br>string</p>              | Type of comment (comment or note)                                           |
| <p><strong>created\_at</strong><br>string</p>       | The date & time the comment was created (UTC timezone)                      |
| <p><strong>updated\_at</strong><br>string</p>       | The date & time the comment was last updated (UTC timezone)                 |
| <p><strong>follower</strong><br>object</p>          | Author of this comment                                                      |
| <p><strong>idea\_id</strong><br>integer</p>         | ID of the associated idea                                                   |
| <p><strong>attachments</strong><br>array</p>        | Files and images attached to this comment                                   |

### Endpoints

You can use the following endpoints to manage Comments

{% openapi src="/files/Qx4G1sIwFj82rUre1l18" path="/comments" method="get" %}
[comments.yaml](https://554427104-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FIpllvC2FiBlBNS7dyV5t%2Fuploads%2Fgit-blob-cc835f4d5b0329342796a1b086c070aca1be2a1d%2Fcomments.yaml?alt=media)
{% endopenapi %}

{% openapi src="/files/Qx4G1sIwFj82rUre1l18" path="/comments" method="post" %}
[comments.yaml](https://554427104-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FIpllvC2FiBlBNS7dyV5t%2Fuploads%2Fgit-blob-cc835f4d5b0329342796a1b086c070aca1be2a1d%2Fcomments.yaml?alt=media)
{% endopenapi %}

{% openapi src="/files/Qx4G1sIwFj82rUre1l18" path="/comments/{commentIdx}" method="post" %}
[comments.yaml](https://554427104-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FIpllvC2FiBlBNS7dyV5t%2Fuploads%2Fgit-blob-cc835f4d5b0329342796a1b086c070aca1be2a1d%2Fcomments.yaml?alt=media)
{% endopenapi %}

{% openapi src="/files/Qx4G1sIwFj82rUre1l18" path="/comments/{commentIdx}" method="delete" %}
[comments.yaml](https://554427104-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FIpllvC2FiBlBNS7dyV5t%2Fuploads%2Fgit-blob-cc835f4d5b0329342796a1b086c070aca1be2a1d%2Fcomments.yaml?alt=media)
{% endopenapi %}


# Statuses

Statuses reflect the current stage of an Idea or feature request for your product as it progresses from suggestion, through development, to completion.

## Status object

Status have the following fields

| Field                                   | Details                                                 |
| --------------------------------------- | ------------------------------------------------------- |
| <p><strong>idx</strong><br>string</p>   | A unique identifier for the idea                        |
| <p><strong>name</strong><br>string</p>  | Name of the status                                      |
| <p><strong>color</strong><br>string</p> | Color associated with the status (displayed in the App) |

### Endpoints

You can use the following endpoints to manage Statuses

#### List statuses

{% openapi src="/files/zGai3A49A2ai8McgxxJw" path="/statuses" method="get" %}
[statuses.yaml](https://554427104-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FIpllvC2FiBlBNS7dyV5t%2Fuploads%2Fgit-blob-3f1ddef76e047ab8a3a71681261d6f067711d1bf%2Fstatuses.yaml?alt=media)
{% endopenapi %}

#### Create a status

{% openapi src="/files/zGai3A49A2ai8McgxxJw" path="/statuses" method="post" %}
[statuses.yaml](https://554427104-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FIpllvC2FiBlBNS7dyV5t%2Fuploads%2Fgit-blob-3f1ddef76e047ab8a3a71681261d6f067711d1bf%2Fstatuses.yaml?alt=media)
{% endopenapi %}

#### Update a status

{% openapi src="/files/zGai3A49A2ai8McgxxJw" path="/statuses/{statusIdx}" method="post" %}
[statuses.yaml](https://554427104-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FIpllvC2FiBlBNS7dyV5t%2Fuploads%2Fgit-blob-3f1ddef76e047ab8a3a71681261d6f067711d1bf%2Fstatuses.yaml?alt=media)
{% endopenapi %}

#### Delete a status

{% openapi src="/files/zGai3A49A2ai8McgxxJw" path="/statuses/{statusIdx}" method="delete" %}
[statuses.yaml](https://554427104-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FIpllvC2FiBlBNS7dyV5t%2Fuploads%2Fgit-blob-3f1ddef76e047ab8a3a71681261d6f067711d1bf%2Fstatuses.yaml?alt=media)
{% endopenapi %}


# Topics

Topics help you organize your Ideas into groups, much like tags. An Ideas can belong to multiple Topics.

### Topic object

Topic have the following fields

| Field                                         | Details                                               |
| --------------------------------------------- | ----------------------------------------------------- |
| <p><strong>idx</strong><br>string</p>         | A unique identifier for the topic                     |
| <p><strong>name</strong><br>string</p>        | Name of the topic                                     |
| <p><strong>idea\_count</strong><br>number</p> | Number of ideas that have been tagged with this Topic |
| <p><strong>order</strong><br>number</p>       | The position of the topic in the topic list           |

### Endpoints

You can use the following endpoints to manage Topics

#### List topics

{% openapi src="/files/aw6EsC91sKBos446IpBT" path="/topics" method="get" %}
[topics.yaml](https://554427104-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FIpllvC2FiBlBNS7dyV5t%2Fuploads%2Fgit-blob-b2558ffec561be87ffecc6ae1d93b1a41987ac45%2Ftopics.yaml?alt=media)
{% endopenapi %}

#### Create a topic

{% openapi src="/files/aw6EsC91sKBos446IpBT" path="/topics" method="post" %}
[topics.yaml](https://554427104-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FIpllvC2FiBlBNS7dyV5t%2Fuploads%2Fgit-blob-b2558ffec561be87ffecc6ae1d93b1a41987ac45%2Ftopics.yaml?alt=media)
{% endopenapi %}

#### Update a topic

{% openapi src="/files/aw6EsC91sKBos446IpBT" path="/topics/{topicIdx}" method="post" %}
[topics.yaml](https://554427104-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FIpllvC2FiBlBNS7dyV5t%2Fuploads%2Fgit-blob-b2558ffec561be87ffecc6ae1d93b1a41987ac45%2Ftopics.yaml?alt=media)
{% endopenapi %}

#### Delete a topic

{% openapi src="/files/aw6EsC91sKBos446IpBT" path="/topics/{topicIdx}" method="delete" %}
[topics.yaml](https://554427104-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FIpllvC2FiBlBNS7dyV5t%2Fuploads%2Fgit-blob-b2558ffec561be87ffecc6ae1d93b1a41987ac45%2Ftopics.yaml?alt=media)
{% endopenapi %}


# Followers

Followers are users who have engaged with your board or are tracking specific Ideas or Announcements for updates. These users are often referred to as tracked users

## Follower object

Followers have the following fields

| Field                                         | Details                                                                             |
| --------------------------------------------- | ----------------------------------------------------------------------------------- |
| <p><strong>idx</strong><br>string</p>         | A unique identifier for the follower                                                |
| <p><strong>name</strong><br>string</p>        | Name of the follower                                                                |
| <p><strong>email</strong><br>string</p>       | Email address of the follower                                                       |
| <p><strong>avatar</strong><br>string</p>      | Profile image of the follower (URL)                                                 |
| <p><strong>attributes</strong><br>object</p>  | Custom attributes for the follower (included when `include_attributes=true`)        |
| <p><strong>companies</strong><br>array</p>    | Companies the follower is associated with (included when `include_attributes=true`) |
| <p><strong>created\_at</strong><br>string</p> | Date the follower was created                                                       |
| <p><strong>updated\_at</strong><br>string</p> | Date the follower was last updated                                                  |

`attributes` and `companies` are only returned when you pass `include_attributes=true`. Each entry in `companies` contains `id` (company source identifier, a string), `name`, `monthly_spend`, and an `attributes` object of custom company attributes.

## Endpoints

You can use the following endpoints to manage Topics

#### List followers

## List followers

> Returns a list of followers

```json
{"openapi":"3.0.0","info":{"title":"Frill API","version":"1.0.0"},"servers":[{"url":"https://api.frill.co/v1"}],"security":[{"BearerAuth":[]},{"BasicAuth":[]},{"ApiKeyQuery":[]}],"components":{"securitySchemes":{"BearerAuth":{"type":"http","scheme":"bearer","description":"API key in Authorization header with Bearer prefix (recommended)"},"BasicAuth":{"type":"http","scheme":"basic","description":"API key as basic auth username (no password required)"},"ApiKeyQuery":{"type":"apiKey","in":"query","name":"api_key","description":"API key as query parameter"}},"parameters":{"PageLimit":{"name":"limit","in":"query","description":"Limits the number of items on a page","schema":{"type":"integer","default":20}},"PageAfter":{"name":"after","in":"query","description":"The after cursor - used for pagination","schema":{"type":"string"}}},"schemas":{"Follower":{"type":"object","required":["idx"],"properties":{"idx":{"type":"string","description":"A unique identifier for the follower"},"name":{"type":"string","description":"Name of the follower"},"email":{"type":"string","description":"Email address of the follower"},"avatar":{"type":"string","description":"Profile image of the follower (URL)"},"attributes":{"type":"object","description":"Custom attributes for the follower (included when include_attributes=true)"},"companies":{"type":"array","description":"Companies the follower is associated with (included when include_attributes=true)","items":{"type":"object","properties":{"id":{"type":"string","description":"Company source identifier"},"name":{"type":"string","description":"Company name"},"monthly_spend":{"type":"number","description":"Monthly spend amount"},"attributes":{"type":"object","description":"Custom attributes for the company"}}}},"created_at":{"type":"string","description":"The date & time the Follower was last updated (UTC timezone)"},"updated_at":{"type":"string","description":"The date & time the Follower was last updated (UTC timezone)"}}},"Pagination":{"type":"object","properties":{"total":{"type":"number"},"before":{"type":"string"},"after":{"type":"string"}}}},"responses":{"400Error":{"description":"Invalid request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"boolean"},"message":{"type":"string"}}}}}}}},"paths":{"/followers":{"get":{"summary":"List followers","description":"Returns a list of followers","operationId":"listFollowers","parameters":[{"$ref":"#/components/parameters/PageLimit"},{"$ref":"#/components/parameters/PageAfter"},{"name":"include_attributes","in":"query","required":false,"schema":{"type":"boolean"},"description":"Include custom attributes in the response"}],"responses":{"200":{"description":"Successfully returned a list of followers","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Follower"}},"pagination":{"$ref":"#/components/schemas/Pagination"}}}}}},"400":{"$ref":"#/components/responses/400Error"}}}}}}
```

#### Search for a follower

## Search followers

> Search for followers by email

```json
{"openapi":"3.0.0","info":{"title":"Frill API","version":"1.0.0"},"servers":[{"url":"https://api.frill.co/v1"}],"security":[{"BearerAuth":[]},{"BasicAuth":[]},{"ApiKeyQuery":[]}],"components":{"securitySchemes":{"BearerAuth":{"type":"http","scheme":"bearer","description":"API key in Authorization header with Bearer prefix (recommended)"},"BasicAuth":{"type":"http","scheme":"basic","description":"API key as basic auth username (no password required)"},"ApiKeyQuery":{"type":"apiKey","in":"query","name":"api_key","description":"API key as query parameter"}},"schemas":{"Follower":{"type":"object","required":["idx"],"properties":{"idx":{"type":"string","description":"A unique identifier for the follower"},"name":{"type":"string","description":"Name of the follower"},"email":{"type":"string","description":"Email address of the follower"},"avatar":{"type":"string","description":"Profile image of the follower (URL)"},"attributes":{"type":"object","description":"Custom attributes for the follower (included when include_attributes=true)"},"companies":{"type":"array","description":"Companies the follower is associated with (included when include_attributes=true)","items":{"type":"object","properties":{"id":{"type":"string","description":"Company source identifier"},"name":{"type":"string","description":"Company name"},"monthly_spend":{"type":"number","description":"Monthly spend amount"},"attributes":{"type":"object","description":"Custom attributes for the company"}}}},"created_at":{"type":"string","description":"The date & time the Follower was last updated (UTC timezone)"},"updated_at":{"type":"string","description":"The date & time the Follower was last updated (UTC timezone)"}}},"Pagination":{"type":"object","properties":{"total":{"type":"number"},"before":{"type":"string"},"after":{"type":"string"}}}},"responses":{"400Error":{"description":"Invalid request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"boolean"},"message":{"type":"string"}}}}}}}},"paths":{"/followers/search":{"get":{"summary":"Search followers","description":"Search for followers by email","operationId":"searchFollowers","parameters":[{"name":"email","in":"query","required":true,"schema":{"type":"string"}},{"name":"include_attributes","in":"query","required":false,"schema":{"type":"boolean"},"description":"Include custom attributes in the response"}],"responses":{"200":{"description":"Successfully returned a list of followers","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Follower"}},"pagination":{"$ref":"#/components/schemas/Pagination"}}}}}},"400":{"$ref":"#/components/responses/400Error"}}}}}}
```

#### Create Follower

## Create a follower

> Create a new follower

```json
{"openapi":"3.0.0","info":{"title":"Frill API","version":"1.0.0"},"servers":[{"url":"https://api.frill.co/v1"}],"security":[{"BearerAuth":[]},{"BasicAuth":[]},{"ApiKeyQuery":[]}],"components":{"securitySchemes":{"BearerAuth":{"type":"http","scheme":"bearer","description":"API key in Authorization header with Bearer prefix (recommended)"},"BasicAuth":{"type":"http","scheme":"basic","description":"API key as basic auth username (no password required)"},"ApiKeyQuery":{"type":"apiKey","in":"query","name":"api_key","description":"API key as query parameter"}},"schemas":{"FollowerCreate":{"type":"object","required":["name","email"],"properties":{"name":{"type":"string","description":"Name of the follower"},"email":{"type":"string","description":"Email address of the follower"},"attributes":{"type":"object","description":"Custom attributes for the follower (key-value pairs)"},"companies":{"type":"array","description":"Array of companies the follower is associated with. Each company must contain id and name. It can also contain any custom fields (e.g., monthly_spend, plan) which are optional.","items":{"type":"object","properties":{"id":{"type":"string","description":"Company identifier"},"name":{"type":"string","description":"Company name"}}}}}},"Follower":{"type":"object","required":["idx"],"properties":{"idx":{"type":"string","description":"A unique identifier for the follower"},"name":{"type":"string","description":"Name of the follower"},"email":{"type":"string","description":"Email address of the follower"},"avatar":{"type":"string","description":"Profile image of the follower (URL)"},"attributes":{"type":"object","description":"Custom attributes for the follower (included when include_attributes=true)"},"companies":{"type":"array","description":"Companies the follower is associated with (included when include_attributes=true)","items":{"type":"object","properties":{"id":{"type":"string","description":"Company source identifier"},"name":{"type":"string","description":"Company name"},"monthly_spend":{"type":"number","description":"Monthly spend amount"},"attributes":{"type":"object","description":"Custom attributes for the company"}}}},"created_at":{"type":"string","description":"The date & time the Follower was last updated (UTC timezone)"},"updated_at":{"type":"string","description":"The date & time the Follower was last updated (UTC timezone)"}}}},"responses":{"400Error":{"description":"Invalid request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"boolean"},"message":{"type":"string"}}}}}}}},"paths":{"/followers":{"post":{"summary":"Create a follower","description":"Create a new follower","operationId":"createFollower","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FollowerCreate","required":["name","email"]}}}},"responses":{"200":{"description":"Successfully created a new follower","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Follower"}}}}}},"400":{"$ref":"#/components/responses/400Error"},"403":{"description":"Custom attributes not enabled (when attributes or companies are provided)","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"boolean"},"message":{"type":"string"},"code":{"type":"string"}}}}}}}}}}}
```

#### Update Follower

## Update a follower

> Update an existing follower

```json
{"openapi":"3.0.0","info":{"title":"Frill API","version":"1.0.0"},"servers":[{"url":"https://api.frill.co/v1"}],"security":[{"BearerAuth":[]},{"BasicAuth":[]},{"ApiKeyQuery":[]}],"components":{"securitySchemes":{"BearerAuth":{"type":"http","scheme":"bearer","description":"API key in Authorization header with Bearer prefix (recommended)"},"BasicAuth":{"type":"http","scheme":"basic","description":"API key as basic auth username (no password required)"},"ApiKeyQuery":{"type":"apiKey","in":"query","name":"api_key","description":"API key as query parameter"}},"schemas":{"FollowerUpdate":{"type":"object","properties":{"name":{"type":"string","description":"Name of the follower (optional)"},"email":{"type":"string","description":"Email address of the follower (optional)"},"attributes":{"type":"object","description":"Custom attributes to merge with existing attributes (key-value pairs, optional)"},"companies":{"type":"array","description":"Array of companies to merge with existing associations (optional). Each company can contain any custom fields (e.g., monthly_spend, plan) which are optional.","items":{"type":"object","properties":{"id":{"type":"string","description":"Company identifier"},"name":{"type":"string","description":"Company name"}}}}}},"Follower":{"type":"object","required":["idx"],"properties":{"idx":{"type":"string","description":"A unique identifier for the follower"},"name":{"type":"string","description":"Name of the follower"},"email":{"type":"string","description":"Email address of the follower"},"avatar":{"type":"string","description":"Profile image of the follower (URL)"},"attributes":{"type":"object","description":"Custom attributes for the follower (included when include_attributes=true)"},"companies":{"type":"array","description":"Companies the follower is associated with (included when include_attributes=true)","items":{"type":"object","properties":{"id":{"type":"string","description":"Company source identifier"},"name":{"type":"string","description":"Company name"},"monthly_spend":{"type":"number","description":"Monthly spend amount"},"attributes":{"type":"object","description":"Custom attributes for the company"}}}},"created_at":{"type":"string","description":"The date & time the Follower was last updated (UTC timezone)"},"updated_at":{"type":"string","description":"The date & time the Follower was last updated (UTC timezone)"}}}},"responses":{"400Error":{"description":"Invalid request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"boolean"},"message":{"type":"string"}}}}}}}},"paths":{"/followers/{followerIdx}":{"post":{"summary":"Update a follower","description":"Update an existing follower","operationId":"updateFollower","parameters":[{"name":"followerIdx","in":"path","required":true,"schema":{"type":"string"},"description":"The unique identifier for the follower"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FollowerUpdate"}}}},"responses":{"200":{"description":"Successfully updated the follower","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Follower"}}}}}},"400":{"$ref":"#/components/responses/400Error"},"403":{"description":"Custom attributes not enabled","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"boolean"},"message":{"type":"string"},"code":{"type":"string"}}}}}},"404":{"description":"Follower not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"boolean"},"message":{"type":"string"},"code":{"type":"string"}}}}}}}}}}}
```

#### Delete a follower

## Delete a follower

> Delete a follower

```json
{"openapi":"3.0.0","info":{"title":"Frill API","version":"1.0.0"},"servers":[{"url":"https://api.frill.co/v1"}],"security":[{"BearerAuth":[]},{"BasicAuth":[]},{"ApiKeyQuery":[]}],"components":{"securitySchemes":{"BearerAuth":{"type":"http","scheme":"bearer","description":"API key in Authorization header with Bearer prefix (recommended)"},"BasicAuth":{"type":"http","scheme":"basic","description":"API key as basic auth username (no password required)"},"ApiKeyQuery":{"type":"apiKey","in":"query","name":"api_key","description":"API key as query parameter"}},"responses":{"200Success":{"description":"Request was successful","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"message":{"type":"string"}}}}}},"400Error":{"description":"Invalid request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"boolean"},"message":{"type":"string"}}}}}}}},"paths":{"/followers/{followerIdx}":{"delete":{"summary":"Delete a follower","description":"Delete a follower","operationId":"deleteFollower","parameters":[{"name":"followerIdx","in":"path","required":true,"schema":{"type":"string"},"description":"The unique identifier for the follower"},{"name":"content","in":"query","required":false,"schema":{"type":"string","enum":["delete","reassign"],"default":"delete"},"description":"Action to take with follower's content: 'delete' removes all content, 'reassign' transfers content to an anonymous user"}],"responses":{"200":{"$ref":"#/components/responses/200Success"},"400":{"$ref":"#/components/responses/400Error"}}}}}}
```


# Votes

Votes are expressions of support or preference that users cast for submitted ideas on your Frill account.

## Vote object

Vote have the following fields

| Field                                         | Details                                            |
| --------------------------------------------- | -------------------------------------------------- |
| <p><strong>idx</strong><br>string</p>         | A unique identifier for the vote                   |
| <p><strong>created\_at</strong><br>string</p> | The date time the vote was created in UTC timezone |
| <p><strong>voter</strong><br>object</p>       | The user this vote is associated with              |
| <p><strong>idea</strong><br>object</p>        | The idea this vote is associated with              |

## Endpoints

You can use the following endpoints to manage Votes

{% openapi src="/files/gXXOsVxsnRWnONuX7593" path="/votes" method="get" %}
[votes.yaml](https://554427104-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FIpllvC2FiBlBNS7dyV5t%2Fuploads%2Fgit-blob-69fa8ade3594ae6e8521671ca08c5e02a2eee488%2Fvotes.yaml?alt=media)
{% endopenapi %}

{% openapi src="/files/gXXOsVxsnRWnONuX7593" path="/votes" method="post" %}
[votes.yaml](https://554427104-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FIpllvC2FiBlBNS7dyV5t%2Fuploads%2Fgit-blob-69fa8ade3594ae6e8521671ca08c5e02a2eee488%2Fvotes.yaml?alt=media)
{% endopenapi %}

{% openapi src="/files/gXXOsVxsnRWnONuX7593" path="/votes" method="delete" %}
[votes.yaml](https://554427104-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FIpllvC2FiBlBNS7dyV5t%2Fuploads%2Fgit-blob-69fa8ade3594ae6e8521671ca08c5e02a2eee488%2Fvotes.yaml?alt=media)
{% endopenapi %}


# Integration Links

Integration links connect an Idea to a record in an external tool — for example the Zendesk tickets or Intercom conversations that prompted a piece of feedback.

## Integration link object

Integration links have the following fields

| Field                                         | Details                                                                                                                                                             |
| --------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| <p><strong>idx</strong><br>string</p>         | A unique identifier for the integration link                                                                                                                        |
| <p><strong>idea\_idx</strong><br>string</p>   | A unique identifier for the idea this link belongs to                                                                                                               |
| <p><strong>type</strong><br>string</p>        | The integration this link points at. Currently `zendesk` or `intercom`. Not validated on write, so other values may be present.                                     |
| <p><strong>link\_ref</strong><br>string</p>   | The identifier of the linked record in the external system — for Zendesk, the ticket ID.                                                                            |
| <p><strong>meta</strong><br>object</p>        | Additional detail supplied by the integration when the link was created. Shape varies by integration and is not validated by Frill, so treat every key as optional. |
| <p><strong>created\_at</strong><br>string</p> | The date & time the link was created in UTC timezone                                                                                                                |
| <p><strong>updated\_at</strong><br>string</p> | The date & time the link was last updated (UTC timezone)                                                                                                            |

One row is returned per external record, each carrying its own `idea_idx`, so an idea with three Zendesk tickets returns three rows.

{% hint style="info" %}
At least one of `idea_idx` or `type` must be supplied. Use `idea_idx` to fetch the links for a single idea, or `type` to page through every link of that integration type across your account and group them by `idea_idx` yourself.
{% endhint %}

### Endpoints

You can use the following endpoint to read Integration Links

{% openapi src="/files/QCtDcTbC6vmEobQaETqR" path="/integration-links" method="get" %}
[integration\_links.yaml](https://554427104-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FIpllvC2FiBlBNS7dyV5t%2Fuploads%2Fgit-blob-556b671dbd3a3493cb0c613bde85cb09047d0ee2%2Fintegration_links.yaml?alt=media)
{% endopenapi %}


# --- Coming soon ---


# Notes

Coming soon ...


# Quick start

## Quick start

#### Getting Started

Use the Frill Script to automatically load all Widgets and Surveys. Get your Frill Script code from the [Frill Script settings page](https://app.frill.co/settings/company/frill-script) and place the script before the closing `</body>` tag on your website.

```html
<!-- Frill (https://frill.co) -->
<script>
  (function(t,r){function s(){var a=r.getElementsByTagName("script")[0],e=r.createElement("script");e.type="text/javascript",e.async=!0,e.src="https://widget.frill.co/v2/container.js",a.parentNode.insertBefore(e,a)}if(!t.Frill){var f=0,i={};t.Frill=function(e,o){var n,l=f++,c=new Promise(function(v,d){i[l]={params:[e,o],resolve(p){n=p,v(p)},reject:d}});return c.destroy=function(){delete i[l],n&&n.destroy()},c},t.Frill.q=i}r.readyState==="complete"||r.readyState==="interactive"?s():r.addEventListener("DOMContentLoaded",s)})(window,document);
  window.Frill('container', {
    key: 'YOUR_SCRIPT_KEY',
    // Extra configuration here... e.g. user identification/sso/callback
  });
</script>
<!-- End Frill -->
```

The Frill Script will automatically load all Widgets and Surveys.

#### React & Frontend Frameworks

If you're using a bundler (**React**, **Next.js**, **Vue**, **Angular**, etc.), install the [`@frillco/script`](https://www.npmjs.com/package/@frillco/script) package. It's a type-safe wrapper that loads the Frill Script for you — no `<script>` tag to add — with dedicated components and hooks for React & Next.js.

```bash
npm install @frillco/script
```

**React & Next.js** get first-class helpers. Render `<FrillScript />` once (e.g. in your root layout) to load your container before hydration:

```tsx
import { FrillScript } from '@frillco/script/react';

// app/layout.tsx — loads your container
<FrillScript container={{ key: 'YOUR_SCRIPT_KEY' }} />;
```

Then control Widgets with hooks like `useWidget`:

```tsx
import { useWidget } from '@frillco/script/react';

function FeedbackButton() {
  // `null` until ready, then re-renders with the instance
  const widget = useWidget({ key: 'YOUR_WIDGET_KEY' });
  return <button onClick={() => widget?.open()}>Feedback</button>;
}
```

For other frameworks (or imperative use in event handlers), call `frill` directly. It loads the Frill Script on first use, so calling it up front loads Frill eagerly:

```ts
import { frill } from '@frillco/script';

await frill('container', {
  key: 'YOUR_SCRIPT_KEY',
  // Optionally identify the current user
  user: { name: 'Ada Lovelace', email: 'ada@example.com' },
});
```

Prefer a plain script tag? You don't need the package — load the [script above](#getting-started) like you would any other and Frill will automatically create and remove Widgets & Surveys.

If you are conditionally loading the Frill Script, or a single Widget/Survey, then you should make sure you are correctly cleaning up any Frill instance(s). For more information, please see our manual loading guide for Widgets which covers this topic in more depth.

Check out our [Widget examples repo](https://github.com/Frill-co/frill-widget-examples) for more complete React & Next.js examples.


# Identifying users

[Guest users](#user-content-fn-1)[^1] can be identified when you first load the Frill Script, or at any time using the `identify` command.

**Important**

* You must provide a valid email.
* Name is optional but recommended
* Guest authentication will fail if:
  * Your Widget/Survey has user/password or SSO authentication enabled
  * The email is associated to an existing user/password account

The simplest way to identify users is with the `container` command. Just define the user as part of your configuration. We recommend identifying with an [SSO token](#sso) when possible as it circumvents any possibe edge cases (see above) that come with guest authentication.

```js
window.Frill('container', {
  key: 'YOUR_SCRIPT_KEY',
  user: {
    email: 'email@domain.com',
    name: 'my user', // optional
    // You can also pass custom variables (attributes and companies) when identifying users
    // attributes: { mrr: 100 },
    // companies : [{ id: 'frill', name: "Frill.co" }] }
  },
});
```

### `Frill('identify')`&#x20;

If your app has client side authentication, you can identify the user using the `identify`  command when they login. The `identify` command will identify the user for all active (and future) Widgets & Surveys.

```js
await window.Frill('identify', {
  email: 'email@domain.com',
  name: 'my user'
  // You can pass custom variables here too...
  // attributes: { mrr: 100 },
  // companies : [{ id: 'frill', name: "Frill.co" }] }
});
```

If the user logs out, you should unidentify them:

```js
await window.Frill('unidentify');
```

### Single Sign On (SSO) <a href="#sso" id="sso"></a>

You can identify SSO users by passing a signed SSO JWT instead of user details. Your company does not need to enable SSO in the platform for this to work.

```typescript
window.Frill('container', {
  key: 'YOUR_SCRIPT_KEY',
  ssoToken: 'SSO_JWT_FROM_SERVER',
})

// Or with the identify command
await window.Frill('identify', { ssoToken: 'SSO_JWT_FROM_SERVER' });
```

[Check out this guide for more information on setting up SSO](/single-sign-on/quick-start)

### Custom attributes

Custom attributes are a powerful way to segment your users inside of Frill. You can pass custom attributes when you identify users, just include an `attributes` and/or `companies` object alongside the user details.

{% hint style="info" %}
You must turn on the "**Allow custom attributes when identifying users with JS**" option from the Frill Script setup identification settings. If you're using SSO you will need to add the attributes as part of the token payload. [**Check out this guide**](https://help.frill.co/article/204-adding-custom-user-attributes-for-segmentation) for more information.
{% endhint %}

```typescript
await window.Frill('identify', {
  email: 'email@domain.com',
  name: 'my user'
  attributes: { mrr: 100, job_title: 'CEO', free_user: false },
  companies : [{ id: 'frill', name: "Frill.co" }] }
});
```

### iFrames

If you're loading your Widget in an iFrame you can still identify users (including SSO) by passing the user details as a query parameter.

#### **Guest users**

You can authenticate guest users by passing their details as the `user` query parameter. The object should be encoded and must have a valid email.

```javascript
// In javascript you would do
const user = encodeURIComponent(JSON.stringify({ name: 'Mitch Guest', email: 'mitch+guest@frill.co' }))
// And it should look like this
// %7B%22name%22%3A%22Mitch%20Guest%22%2C%22email%22%3A%22mitch%2Bguest%40frill.co%22%7D
```

```html
// Then load the Widget iframe
<iframe src="https://YOUR_FRILL_DOMAIN/embed/widget/YOUR_WIDGET_KEY?user=%7B%22name%22%3A%22Mitch%20Guest%22%2C%22email%22%3A%22mitch%2Bguest%40frill.co%22%7D" sandbox="allow-same-origin allow-scripts allow-top-navigation allow-popups allow-forms allow-popups-to-escape-sandbox" style="border: 0px; outline: 0px; width: 680px; height: 460px;"></iframe>
// It also works for Surveys
<iframe src="https://YOUR_FRILL_DOMAIN/embed/survey/YOUR_SURVEY_KEY?user=%7B%22name%22%3A%22Mitch%20Guest%22%2C%22email%22%3A%22mitch%2Bguest%40frill.co%22%7D" sandbox="allow-same-origin allow-scripts allow-top-navigation allow-popups allow-forms allow-popups-to-escape-sandbox" style="border: 0px; outline: 0px; width: 680px; height: 460px;"></iframe>
```

#### SSO users

To identify SSO users in an iFrame, pass your signed SSO JWT as the `ssoToken` query parameter.

```
// Then load the Widget iframe
<iframe src="https://YOUR_FRILL_DOMAIN/embed/widget/YOUR_WIDGET_KEY?ssoToken=SSO_JWT_FROM_SERVER" sandbox="allow-same-origin allow-scripts allow-top-navigation allow-popups allow-forms allow-popups-to-escape-sandbox" style="border: 0px; outline: 0px; width: 680px; height: 460px;"></iframe>
// It also works for Surveys
<iframe src="https://YOUR_FRILL_DOMAIN/embed/survey/YOUR_SURVEY_KEY?ssoToken=SSO_JWT_FROM_SERVER" sandbox="allow-same-origin allow-scripts allow-top-navigation allow-popups allow-forms allow-popups-to-escape-sandbox" style="border: 0px; outline: 0px; width: 680px; height: 460px;"></iframe>
```

[^1]: Your widget (or company) must have Guest or Anonymous authentication enabled for this to work.


# Widgets

The Frill API allows you to access, customize, and control any Widget loaded by the Frill Script. Using the Frill API is simple once you have a reference to a Widget. To do this, you first need to know the **Key**, which you can find in your Widget dashboard, or on the edit screen.

### `Frill('widget')`

The easiest way to access a Widget is by calling the `widget` command with the Widget **Key.** If the Widget is already loaded, the function will return the existing instance, making it safe to call multiple times.

```js
const widget = await window.Frill('widget', {
  key: 'YOUR_WIDGET_KEY', // <-- Add Widget key here
  callbacks: {
    onReady(widget) {
      // Optional callback if you prefer this syntax
      console.log(widget);
    },
  },
});
```

### **Using container `onReady` callback**

Another option is to providing an `onReady` callback in your script configuration. This callback will run once when ready and will receive the Widget instance. To do this, include a custom `widgets` configuration when you load your script, using the corresponding Widget Key.

```js
window.Frill('container', {
  key: 'YOUR_CONTAINER_KEY',
  widgets: [
    {
      key: 'YOUR_WIDGET_KEY', // <-- Add Widget key here
      callbacks: {
        onReady(widget) {
          // This function is called when the Frill Widget is fully loaded and ready for use
          console.log(widget);
        },
      },
    },
  ],
});
```


# Manual loading

If you want to manually/conditionally load a Widget (or Survey) you still need to call `Frill('container')` as early as possible.

### **Disable auto loading**

You can control/disable automatic loading of Widgets & Surveys when you call the `container` command with the `autoLoad` option.

```js
window.Frill('container', {
  key: 'YOUR_SCRIPT_KEY',
  // Pass false to disable automatic Widget and Survey loading
  autoLoad: false, // Also accepts: 'widgets' or 'surveys'
});
```

The `autoLoad` option accepts:

* `boolean` - If `false` then nothing will not be loaded
* `"widgets"` - Only Widgets will automatically load
* `"surveys"` - Only Surveys will automatically load

{% hint style="info" %}
Remember, you can conditionally show a Widget, or Survey, by configuring URL and audience targeting rules (user segmentation) in the settings page. There is also a "None" Launcher option which leaves it up to you to [open & close](/frill-script/widgets/controlling-the-widget) the Widget.
{% endhint %}

When you're ready to show the Widget, call the `widget` command with the Widget Key.

```typescript
const widget = await window.Frill('widget', {
  key: 'YOUR_WIDGET_KEY', // <-- Add Widget key here
  callbacks: {
    onReady(widget) {
      // Optional callback if you prefer this syntax
      console.log(widget);
    },
  },
});
```

{% hint style="warning" %}
Using the `widget` command to load a Widget will ignore all targeting rules (e.g. URL matching and segmentation).
{% endhint %}


# Embed Widgets

### Embed

If your Widget type is **Embed** you will need to insert HTML in your website where the Widget needs to appear. You can get this HTML from the Widget settings page.

```html
<!-- Frill Widget (https://frill.co) -->
<div data-frill-widget="YOUR_WIDGET_KEY" style="width: 340px; height: 460px;"></div>
<!-- End Frill Widget -->
```

{% hint style="info" %}
You still need to call `Frill('container')` for embedded Widgets to load, unless you are manually loading Widgets with the `Frill('widget')` command.
{% endhint %}

[Identified users](/frill-script/identifying-users) work like normal with Embed Widgets (not iFrames), you don't need to do anything special.

### iFrames

It's possible to load any Widget in an iFrame without using the [Frill Script](#user-content-fn-1)[^1]. Every Widget has a unique embed URL that you can get from the Widget Dashboard.

```html
<!-- Frill Widget (https://frill.co) -->
<iframe src="https://YOUR_FRILL_DOMAIN/embed/widget/YOUR_WIDGET_KEY" sandbox="allow-same-origin allow-scripts allow-top-navigation allow-popups allow-forms allow-popups-to-escape-sandbox" style="border: 0px; outline: 0px; width: 450px; height: 400px;"></iframe>
<!-- End Frill Widget -->
```

Check out our [identifying users in iFrames example](/frill-script/identifying-users#iframes) if you need to identify users.

[^1]:


# Controlling the Widget

### Methods

Once you have [access to a Widget instance](/frill-script/widgets#frill-widget) there are methods you can use to control it.

#### `widget.open()`

Open the Widget.

#### `widget.close()`

Close the Widget.

#### `widget.toggle()`

Toggle (open/close) the Widget.

#### `widget.destroy()`

Destroy the Widget and remove it from the DOM.

#### `widget.viewSection(section: 'ideas' | 'roadmap' | 'announcements')`&#x20;

Route the Widget to a section.

#### `widget.viewAnnouncement(announcementSlug: string)`&#x20;

Route to an Announcement in the Widget. The `announcementSlug` can be found in the URL from the Announcement page.

#### `widget.viewIdea(ideaSlug: string)`&#x20;

Route to an Idea in the Widget. The `ideaSlug` can be found in the URL from the Idea page.

#### `widget.createIdea(defaultValues?: { name?: string, topics?: string[] })`&#x20;

Route the Widget to the Create Idea form. Accepts an optional default values object for the Idea name and topics. Topic IDs can be found in the URL if you filter your Ideas board in the platform.

#### `widget.setBadgeCount(count: number)`&#x20;

Set the [Widget Launcher badge](#user-content-fn-1)[^1] count. If 0 the badge will be removed.

### Events & Callbacks

The Widget will emit different events that you can subscribe to. You can subscribe to events when you call the `widget` method, or use the `widget.events.on` function once you have the Widget instance.

Callbacks are defined by:

```typescript
type FrillWidgetCallbacks = {
  /**
   * Fired when the Widget is ready and Announcements have loaded
   * widget.events.on('ready', () => {})
   */
  onReady(widget: FrillWidget) {},
  /**
   * Fired after the Widget has been opened
   * widget.events.on('open', () => {})
   */
  onOpen() {},
  /**
   * Fired after the Widget has been closed
   * widget.events.on('close', () => {})
   */
  onClose() {},
  /**
   * Fired when the Widget is destroyed
   * widget.events.on('destroy', () => {})
   */
  onDestroy() {},
  /**
   * Fired when a user logs in
   * widget.events.on('login', () => {})
   */
  onLogin(event: { user: { name: string; email: string } }) {},
  /**
   * Fired when a user logs out
   * widget.events.on('logout', () => {})
   */
  onLogout() {},
  /**
   * Fired after the badge count changes
   * widget.events.on('badgeCount', () => {})
   */
  onBadgeCount(event: {
    announcements: { idx: string; slug: string; published_at: string }[];
    count: number;
  }) {},
  /**
   * Fired after a boosted Announcement notification has been opened
   * widget.events.on('notificationOpen', () => {})
   */
  onNotificationOpen() {},
  /**
   * Fired after a boosted Announcement notification has been closed
   * widget.events.on('notificationClose', () => {})
   */
  onNotificationClose() {},
};

```

Here's an example of how you would subscribe to the `onBadgeCount` event.

```typescript
const widget = await window.Frill('widget', {
  key: 'YOUR_WIDGET_KEY', // <-- Add Widget key here
  callbacks: {
    // Add callback functions here, e.g.
    onBadgeCount({ count }) {
      console.log(`Widget has ${count} unread Announcements`);
    }
  },
});
```

Or you can use `widget.events` to subscribe/unsubscribe at any time:

```typescript
const unsubscribe = widget.events.on('badgeCount', () => {
  console.log(`Widget has ${count} unread Announcements`);
});
```

[^1]: The Widget Launcher is the button (Floating, Tab or Custom) that launches the Widget.


# Surveys

Surveys will automatically load as part of the `container` command.

If your [triggering the Survey with code](/frill-script/surveys/trigger-with-code) you will need to use the `survey` command with the **Survey** **Key**. You can find the **Key** in your Survey dashboard, or on the edit screen.

{% hint style="info" %}
Although it's not required, it's recommended that you [**identify users**](/frill-script/identifying-users) for best results with Surveys. The **Frill Script** uses cookies for session data.
{% endhint %}

Please read the [manual loading documentation for Widgets](/frill-script/widgets/manual-loading) for instructions on how to disable automatic loading of all Surveys in the Frill Script.


# Trigger with code

Use the  `survey` command with the **Key** to trigger a Survey. If the Survey is already loaded, the function will return the existing instance, making it safe to call multiple times.

```js
const survey = await window.Frill('survey', {
  key: 'YOUR_SURVEY_KEY', // <-- Add Survey key here
  callbacks: {
    onReady(survey) {
      console.log(survey);
    },
  },
});
```

Triggering a Survey will ignore all targeting rules (e.g. URL matching and segmentation), but frequency (re-show) rules will still apply unless you have enabled the **"Ignore frequency rules when triggered with code"** option.

The Survey will not open if another survey is already open, even if you have chosen to ignore frequency rules. You can skip this check with the [force option](#force-show).

### Force show

You can force a Survey to open by setting `force: true` in your `survey` command.

```typescript
const survey = await window.Frill('survey', {
  key: 'YOUR_SURVEY_KEY', // <-- Add Survey key here
  force: true,
});
```


# Configuration

### Themes

Use the `config` command to set the theme all (existing and future) Widgets and Surveys.

```typescript
Frill('config', {
  theme: 'dark' // light, dark, navy, purple
});

// Reset
Frill('config', { theme: null });
```

### Languages

Widgets and Surveys will use the current browser language by default, but you can override the language by specifying one in your config.

```js
window.Frill('container', {
  key: 'YOUR_SCRIPT_KEY',
  language: 'es',
});
```

If your company does not have the specified language it will fallback to the company default.


# Debugging

Not sure why a Widget or Survey isn't showing? Call `Frill('help')` to get a summary of all Frill instances including user details.

### Preview mode

When preview mode is enabled all Surveys will show regardless of Location or Frequency rules. Submitting the Survey preview mode will not record the response. This is a good way to see how a Survey will appear if you have already submitted it.

```typescript
// All surveys will load in preview mode
window.Frill('container', {
  key: 'YOUR_SCRIPT_KEY',
  previewMode: true,
});

// Load a single survey in preview mode
window.Frill('survey', {
  key: 'YOUR_SURVEY_KEY',
  previewMode: true,
});
```

{% hint style="info" %}
A Survey will not appear if it has Audience rules (segmentation) and you have not identified a user in that segment, unless the Survey was loaded manually with the `survey` command.
{% endhint %}

### Disable warnings

The Frill Script has additional developer warnings which can be disabled at any time.

```typescript
window.Frill('container', {
  key: 'YOUR_SCRIPT_KEY',
  disableWarnings: true,
});
```

### Error handling

To guard your application from any Frill related errors, you can guard callbacks by checking `window.Frill.ready` (which is set to `true` once the Frill SDK script has loaded). This should only be used if you are [manually loading](/frill-script/widgets/manual-loading) (and opening/closing) widgets.

```tsx
<button type="button" onClick={() => {
  if (window.Frill?.ready) {
    // Show frill widget here...
    return;
  }
  
  // Otherwise, send user to frill portal
}}>
  Give feedback
</button>
```


# Quick start

{% embed url="<https://help.frill.co/article/65-sso-implementation>" %}


