---
title: Using the Hearken API to integrate the EMS with other tools
description: Hearken API V1 Documentation
---

[Skip to content](https://help.indiegraf.com/using-the-hearken-api-to-integrate-the-ems-with-other-tools#main-content)

English

Show submenu for translations

[Publisher Portal](https://portal.indiegraf.com/tickets?hsLang=en)

[![Indiegraf-PrimaryLogo-FullColor-1](https://help.indiegraf.com/hs-fs/hubfs/Indiegraf-PrimaryLogo-FullColor-1.png?width=262&height=60&name=Indiegraf-PrimaryLogo-FullColor-1.png)](https://help.indiegraf.com/?hsLang=en)

Open main navigation

Close main navigation

- English
  
  Show submenu for translations
- [Publisher Portal](https://portal.indiegraf.com/tickets)

 How can we help you?

- There are no suggestions because the search field is empty.

1. [Indiegraf](https://help.indiegraf.com/?hsLang=en)
2. [Hearken](https://help.indiegraf.com/hearken?hsLang=en)
3. [Setting up APIs and webhooks](https://help.indiegraf.com/hearken?hsLang=en#setting-up-apis-and-webhooks)

March 5, 2026

# Using the Hearken API to integrate the EMS with other tools

## Hearken API V1 Documentation

#### **General Request Format**

Base URL: `https://api.wearehearken.com/api/v1/`

Only HTTPS connections are allowed.

#### **Authentication**

You must obtain an API key. At present there is no self-service process. Please contact [support@wearehearken.com](mailto:support@wearehearken.com) to request an API key.

The preferred method of authentication is to send your API key in an authorization header within your request

Header Format: `Authorization: Bearer <api-key>`

#### **Getting an API Key**

API Keys are scoped to the permissions of the EMS user that owns the key. In the future it will be possible to further constrain these permissions.

#### **Errors**

The following Error codes may be returned from the API

- 401 Not Authorized - when request does not have valid credentials
- 403 Forbidden - when user is not allowed to perform the requested action
- 400 Bad Request - when there is an error with the client request
- 404 Not Found - when client requests a resource that does not exist
- 5xx - internal server errors

The content type of an error response is always application/json and returns an object that contains an `error` key, with some description of the error.

#### **Request Parameters**

Request parameters should be sent as query string parameters. Some common request parameters:

```
_limit
```

 - Limit number of results returned to client with each request.

```
_offset
```

 - Skip this many results before returning results to client.

```
_select
```

 - Limit response to the given top-level fields (see "Responses" below)

**Response Format**

#### **Example Response Collection**

{  
    "\_type": "collection",  
    "total\_objects": 229,  
    "offset": 0,  
    "data": \[   
      // list of items here...  
    \],  
    "limit": 30,  
    "next\_page": "[https://api.wearehearken.com/api/v1/questions?\_limit=30&\_offset=30"](https://api.wearehearken.com/api/v1/questions?_limit=30&_offset=30)  
}

The `total_objects` key contains total number of objects, that match the given query, regardless of whether `_offset` or `_limit` have been specified.

The `offset` and `limit` keys are provided to make it easy to calculate pagination interfaces

The `data` key contains an array of result items (See below for individual formats)

Timestamps are in UTC, given as milliseconds since midnight on January 1st, 1970.

 

#### **Questions**

#### **Listing**

Endpoint: `https://api.wearehearken.com/api/v1/questions`

Method: `GET`

#### Question Request Parameters

```
organization_id
```

 - Limit results to the given comma-separated list of organization IDs. By default, questions from all accessible organizations will be returned

```
list_id
```

 - limit results to the given comma-separated list of question list ids. By default, questions will not be filtered by list

```
source
```

 - limit results to the given source. Example: `source=prompt_embed:123` would return questions that were submitted from an embed with the ID `123`

```
quarantined
```

 - (boolean) - limit results to or exclude quarantined questions. User must have the proper privileges to search by `quarantined=true`

```
created_at
```

 - filter by creation time, given as milliseconds since midnight on January 1st, 1970. Ranges and inequalities may be specified by providing one or more of the following keys:

- created\_at\_gte - created\_at is greater than or equal to given value
- created\_at\_lte - created\_at is less than or equal to given value
- created\_at\_gt - created\_at is greater than given value
- created\_at\_lt - created\_at is less than given value

#### **Format of Individual Question Response**

{  
  "id": 12345678,  
  "prompt": "What do you wonder about running for Congress that you'd like Jenny to cover on the podcast? ",  
  "embed\_name": "API Demonstration Embed",  
  "name": "Jane",  
  "email": "[jane.doe@example.com](mailto:jane.doe@example.com)",  
  "opt\_in\_response": true,  
  "anonymous": false,  
  "display\_text": "I wonder what the stats are on beating an incumbent based on age of population?  ",  
  "original\_text": "I wonder what the stats are on beating an incumbent based on age of population?  ",  
  "approved": false,  
  "deleted": false,  
  "quarantined": false,

"hidden": false,

"source\_url": "[https://www.wearehearken.com/faq](https://www.wearehearken.com/faq)",  
  "reporter": "Codey M.",  
  "notes": "Review this question at next planning meeting",  
  "organization": {  
    "id": 2,  
    "name": "API Example",  
    "slug": "api-example",  
    "\_type": "organization",  
    "created\_at": 1429064494000,  
    "updated\_at": 1509740787769  
  },  
  "question\_status": {  
    "id": 123,  
    "name": "New",  
    "\_type": "question\_status",  
    "created\_at": 1468430366771,  
    "updated\_at": 1468430366771  
  },  
  "lists": {  
    "\_type": "collection",  
    "total\_objects": 1,  
    "offset": 0,  
    "data": \[  
      {  
        "id": 1234,  
        "name": "API Example List",  
        "question\_count": 1,  
        "\_type": "list",  
        "created\_at": 1570451318186,  
        "updated\_at": 1570451318186  
      }  
    \],  
    "limit": 10  
  },  
  "postcode": "98121",  
  "source": "prompt\_embed:123",  
  "custom\_fields": \[  
    {  
      "name": "Postal Code",  
      "value": "98121",  
      "type": "postal\_code",  
      "required": true  
    }  
  \],  
  "organization\_audience\_member": {  
    "id": 12345,  
    "email": "[jane.doe@example.com](mailto:jane.doe@example.com)",  
    "\_type": "organization\_audience\_member",  
  },  
  "created\_at": 1572561753652,  
  "updated\_at": 1572561753652,  
  "\_type": "question"  
}

#### **Updating**

Endpoint: `https://api.wearehearken.com/api/v1/questions/<id>`

Method: `PATCH`

#### **Allowed Request body parameters**

Only send the fields that you need to update. The user associated with the API key must have appropriate permissions.

```
quarantined
```

 - Boolean. Whether or not this question should be considered quarantined.

```
deleted
```

 - Boolean. Whether or not this question should be considered deleted.

```
approved
```

 - Boolean. When moderation is in effect, setting to `true` will approve this question.

#### **Response**

On success, the API will return a `200` status along with the serialized output of the updated record.

#### **Audience Members**

Endpoint: `https://api.wearehearken.com/api/v1/organizations/<organization_id>/audience_members`

Method: `GET`

#### Audience Member Request Parameters

```
organization_id
```

 - **REQUIRED.** Limit results to the given organization ID.

```
opt_in_list_id
```

 - limit results to audience members given opt-in list ID. Only single values allowed.

#### **Sorting**

Sorting is available on any combination of the following fields, by passing comma-separated field names in the `_sort` query parameter.

```
created_at
```

, `first_activity_at`, `last_activity_at`, `question_count`, `vote_count`, `opt_in_list_membership_count`

Sorting is ascending by default. Descending sort on a column is available by prepending a `-` to the column name.

**Example:** Retrieve audience members sorted by decreasing question count, then by earliest activity: `?_sort=-question_count,first_activity_at`

#### **Timestamps**

Filter by timestamps, given as milliseconds since midnight on January 1st, 1970. Ranges and inequalities may be specified by providing one or more of the following keys:

- \<name\> (no suffix) - \<name\> is equal to the given value
- \<name\>\_gte - \<name\> is greater than or equal to given value
- \<name\>\_lte - \<name\> is less than or equal to given value
- \<name\>\_gt - \<name\> is greater than given value
- \<name\>\_lt - \<name\> is less than given value

The following timestamp fields are recognized:

- created\_at - creation date
- first\_activity\_at - time of first activity by this audience member, within the restrictions of the current query
- last\_activity\_at - time of last activity by this audience member, within the restrictions of the current query

#### **Counts**

Filter by counts, given as an integer. Ranges and inequalities may be specified by providing one or more of the following keys:

- \<name\> (no suffix) - \<name\> is equal to the given value
- \<name\>\_gte - \<name\> is greater than or equal to given value
- \<name\>\_lte - \<name\> is less than or equal to given value
- \<name\>\_gt - \<name\> is greater than given value
- \<name\>\_lt - \<name\> is less than given value

The following count fields are recognized:

- question\_count - count of questions asked by this audience member
- vote\_count - count of votes cast by this audience member
- opt\_in\_list\_membership\_count - count of opt-in list memberships

#### **Format of Individual Audience Member Response**

{  
  "id": 12345,  
  "email": "[jane.doe@example.com](mailto:jane.doe@example.com)",  
  "question\_count": 3,  
  "vote\_count": 2,  
  "opt\_in\_list\_membership\_count": 1,  
  "first\_activity\_at": 1520358715522,  
  "last\_activity\_at": 1572561753652,  
  "organization": {  
    "id": 2,  
    "name": "API Example",  
    "slug": "api-example",  
    "\_type": "organization",  
    "created\_at": 1429064494000,  
    "updated\_at": 1509740787769  
  },  
  "first\_activity": "March 6, 2018",  
  "last\_activity": "October 31, 2019",  
  "earliest\_activity\_at": 1520358715522,  
  "latest\_activity\_at": 1572561753652,  
  "opt\_in\_lists": \[  
    {  
      "id": 1,  
      "name": "All newsletter opt-ins",  
      "metadata": "External CRM ID 12",  
      "earliest\_opt\_in\_at": 1520358715522,  
      "latest\_opt\_in\_at": 1572561753652,  
      "latest\_opt\_in\_text": "Sign me up for your newsletter!",  
      "\_type": "opt\_in\_list\_membership"  
    }  
  \],  
  "\_type": "organization\_audience\_member",  
  "created\_at": 1520358715522,  
  "updated\_at": 1572561753652  
},

#### **Quarantine Settings**

Quarantine Settings are per-EMS settings that can be applied to `text` (submission text), `ip` (client IP address), and `email` (submitter's email address) fields.

#### **Listing**

Endpoint: `/api/v1/organizations/<organization_id>/quarantine_settings`

Method: `GET`

#### **Response Format:**

{  
    "\_type": "collection",  
    "total\_objects": 3,  
    "offset": 0,  
    "limit": 10,  
    "data": \[  
        {  
            "\_type": "organization\_quarantine\_setting",  
            "organization": {  
                "\_type": "organization",  
                "created\_at": 1580831134772,  
                "updated\_at": 1581459626449,  
                "id": 1,  
                "name": "Demo Website",  
                "slug": "demo-website"  
            },  
            "words": \[  
                "new",  
                "text",  
                "words"  
            \],  
            "use\_list": "custom",  
            "id": "text",  
            "enabled": true  
        },  
        {  
            "\_type": "organization\_quarantine\_setting",  
            "organization": {  
                "\_type": "organization",  
                "created\_at": 1580831134772,  
                "updated\_at": 1581459626449,  
                "id": 1,  
                "name": "Demo Website",  
                "slug": "demo-website"  
            },  
            "words": \[\],  
            "use\_list": "default",  
            "id": "ip",  
            "enabled": false  
        },  
        {  
            "\_type": "organization\_quarantine\_setting",  
            "organization": {  
                "\_type": "organization",  
                "created\_at": 1580831134772,  
                "updated\_at": 1581459626449,  
                "id": 1,  
                "name": "Demo Website",  
                "slug": "demo-website"  
            },  
            "words": \[\],  
            "use\_list": "default",  
            "id": "email",  
            "enabled": false  
        }  
    \]  
}

#### **Updating**

Endpoint: `/api/v1/organizations/<organization_id>/quarantine_settings/<id>`

Method: `PATCH`

**Note:** The `id` field for a quarantine setting is a string, such as `ip`, `text`, `email`, not a numeric id.

The following fields may be specified in the request body:

```
words
```

 - a list of words to look for in the given field(s) that will mark the submission as quarantined

```
use_list
```

 - one of `default` or `custom`. Specifying `default` will undo any custom word list you have provided and fall back to the system default.

```
enabled
```

 - whether to enable or disable matching for the field refernced by `id`. If `words` is not specified, does not update the word list.

Example: incoming submissions with any of the words spam, href, and http will be marked quarantined

PATCH /api/v1/organizations/12345/quarantine\_settings/text  
​  
{ "enabled": true, "words": \["spam", "href", "http"\], "use\_list":"custom" }

Example: use the default/system email address matchlist for incoming submissions

PATCH /api/v1/organizations/12345/quarantine\_settings/email  
​  
{ "use\_list":"default" }

Example: turn off matching for ip addresses on incoming submissions

PATCH /api/v1/organizations/12345/quarantine\_settings/ip  
​  
{ "enabled": false }

#### **Response**

Upon successful update, a `200` status will be returned, along with the updated record. Otherwise, an error will be returned.

- [Welcome to Indiegraf](https://help.indiegraf.com/welcome-to-indiegraf?hsLang=en)
- [Onboarding](https://help.indiegraf.com/onboarding?hsLang=en#main-content)

    - [Quick Start Guide](https://help.indiegraf.com/onboarding?hsLang=en#quick-start-guide)
- [Indie Website](https://help.indiegraf.com/indie-website?hsLang=en#main-content)

    - [Analytics & Growth](https://help.indiegraf.com/indie-website?hsLang=en#analytics-growth)
    - [Audience Engagement & Forms](https://help.indiegraf.com/indie-website?hsLang=en#audience-engagement-forms)
    - [Configuring your Homepage](https://help.indiegraf.com/indie-website?hsLang=en#configuring-your-homepage)
    - [Content Creation & Management](https://help.indiegraf.com/indie-website?hsLang=en#content-creation-management)
    - [Design & Page Layout](https://help.indiegraf.com/indie-website?hsLang=en#design-page-layout)
    - [Indiegraf Pay](https://help.indiegraf.com/indie-website?hsLang=en#indiegraf-pay)
    - [Media & Visual Assets](https://help.indiegraf.com/indie-website?hsLang=en#media-visual-assets)
    - [Paywall Solution: Pelcro](https://help.indiegraf.com/indie-website?hsLang=en#paywall-solution-pelcro)
    - [SEO and Google Tools](https://help.indiegraf.com/indie-website?hsLang=en#seo-and-google-tools)
    - [Site Administration](https://help.indiegraf.com/indie-website?hsLang=en#site-administration)
    - [Technical Questions](https://help.indiegraf.com/indie-website?hsLang=en#technical-questions)
- [Indie Email](https://help.indiegraf.com/indie-email?hsLang=en#main-content)

    - [Quickstart Video Guide: Indie Email](https://help.indiegraf.com/indie-email?hsLang=en#quickstart-video-guide-indie-email)
    - [Account Setup & Foundations](https://help.indiegraf.com/indie-email?hsLang=en#account-setup-foundations)
    - [Contact Management](https://help.indiegraf.com/indie-email?hsLang=en#contact-management)
    - [Creating Campaigns](https://help.indiegraf.com/indie-email?hsLang=en#creating-campaigns)
    - [Sending Campaigns & Automation](https://help.indiegraf.com/indie-email?hsLang=en#sending-campaigns-automation)
    - [Troubleshooting & Deliverability](https://help.indiegraf.com/indie-email?hsLang=en#troubleshooting-deliverability)
    - [Analytics](https://help.indiegraf.com/indie-email?hsLang=en#analytics)
- [Indie Ads Manager (IAM)](https://help.indiegraf.com/indie-ads-manager-iam?hsLang=en#main-content)

    - [Reporting & Statistics](https://help.indiegraf.com/indie-ads-manager-iam?hsLang=en#reporting-statistics)
    - [Essential Articles](https://help.indiegraf.com/indie-ads-manager-iam?hsLang=en#essential-articles)
    - [Native Ad Templates](https://help.indiegraf.com/indie-ads-manager-iam?hsLang=en#native-ad-templates)
    - [Quick Start Guide](https://help.indiegraf.com/indie-ads-manager-iam?hsLang=en#quick-start-guide)
    - [Payment Solutions](https://help.indiegraf.com/indie-ads-manager-iam?hsLang=en#payment-solutions)
- [Hearken](https://help.indiegraf.com/hearken?hsLang=en#main-content)

    - [Planning and Preparing to Launch with Hearken](https://help.indiegraf.com/hearken?hsLang=en#planning-and-preparing-to-launch-with-hearken)
    - [Collecting Questions](https://help.indiegraf.com/hearken?hsLang=en#collecting-questions)
    - [Collecting Votes](https://help.indiegraf.com/hearken?hsLang=en#collecting-votes)
    - [Reporting an Answer](https://help.indiegraf.com/hearken?hsLang=en#reporting-an-answer)
    - [Spreading Hearken Across Your Organization](https://help.indiegraf.com/hearken?hsLang=en#spreading-hearken-across-your-organization)
    - [Creating and Customizing an Embed](https://help.indiegraf.com/hearken?hsLang=en#creating-and-customizing-an-embed)
    - [Workflow Features](https://help.indiegraf.com/hearken?hsLang=en#workflow-features)
    - [Platform Features](https://help.indiegraf.com/hearken?hsLang=en#platform-features)
    - [Setting Up Notifications and Alerts](https://help.indiegraf.com/hearken?hsLang=en#setting-up-notifications-and-alerts)
    - [Setting up APIs and webhooks](https://help.indiegraf.com/hearken?hsLang=en#setting-up-apis-and-webhooks)
    - [Data collection and exporting](https://help.indiegraf.com/hearken?hsLang=en#data-collection-and-exporting)
    - [Automatic categorization](https://help.indiegraf.com/hearken?hsLang=en#automatic-categorization)
    - [Social, outreach, and marketing](https://help.indiegraf.com/hearken?hsLang=en#social-outreach-and-marketing)
    - [Events and In-Person Engagement](https://help.indiegraf.com/hearken?hsLang=en#events-and-in-person-engagement)
    - [Monetizing your Hearken work](https://help.indiegraf.com/hearken?hsLang=en#monetizing-your-hearken-work)
- [Indiegraf Experts](https://help.indiegraf.com/indiegraf-experts?hsLang=en)
- [Indiegraf Product Release Notes](https://help.indiegraf.com/indiegraf-product-release-notes?hsLang=en#main-content)

    - [Product Demo Videos](https://help.indiegraf.com/indiegraf-product-release-notes?hsLang=en#product-demo-videos)
- [General](https://help.indiegraf.com/general?hsLang=en)
- [RevEngine](https://help.indiegraf.com/revengine?hsLang=en#main-content)

    - [Getting Started with RevEngine](https://help.indiegraf.com/revengine?hsLang=en#getting-started-with-revengine)
    - [International giving](https://help.indiegraf.com/revengine?hsLang=en#international-giving)
    - [RevEngine Core](https://help.indiegraf.com/revengine?hsLang=en#revengine-core)
    - [Organization management](https://help.indiegraf.com/revengine?hsLang=en#organization-management)
    - [Working with Stripe](https://help.indiegraf.com/revengine?hsLang=en#working-with-stripe)
    - [Contribution page basics](https://help.indiegraf.com/revengine?hsLang=en#contribution-page-basics)
    - [Customizing your page](https://help.indiegraf.com/revengine?hsLang=en#customizing-your-page)
    - [Branding and images](https://help.indiegraf.com/revengine?hsLang=en#branding-and-images)
    - [Advanced page settings & features](https://help.indiegraf.com/revengine?hsLang=en#advanced-page-settings-features)
    - [Portal & contribution management](https://help.indiegraf.com/revengine?hsLang=en#portal-contribution-management)
    - [Contribution data](https://help.indiegraf.com/revengine?hsLang=en#contribution-data)
    - [Analytics and Reporting](https://help.indiegraf.com/revengine?hsLang=en#analytics-and-reporting)
    - [Best Practices and Recommendations](https://help.indiegraf.com/revengine?hsLang=en#best-practices-and-recommendations)
    - [Receipts, reminders and transactional communications](https://help.indiegraf.com/revengine?hsLang=en#receipts-reminders-and-transactional-communications)
    - [Security & Legal](https://help.indiegraf.com/revengine?hsLang=en#security-legal)
    - [Common issues](https://help.indiegraf.com/revengine?hsLang=en#common-issues)

[![Indiegrf](https://help.indiegraf.com/hs-fs/hubfs/Indiegraf-SecondaryLogo-DarkNavy-2.png?width=260&height=130&name=Indiegraf-SecondaryLogo-DarkNavy-2.png "Indiegrf")](https://help.indiegraf.com/?hsLang=en)

Indiegraf is on a mission to make local news entrepreneurship a great career and life choice for journalists everywhere.

<https://www.instagram.com/indiegrafmedia> <https://www.twitter.com/indiegrafmedia> <https://www.facebook.com/indiegrafmedia> <https://www.linkedin.com/indiegraf-media> [mailto:support@indiegraf.com](mailto:support@indiegraf.com)

Copyright © 2025, Indiegraf Media