docs

Bluelink API Specs (BETA)

How Bluelink likes to read and write data for our awesome partners!

Table of Contents

Overview

This document covers two primary concepts: Data formats and preferred API specs for reading and writing data.

These specs are strong preferences, but not hard requirements. Building to these specs will

  1. make it fast, easy, and reliable for Bluelink to connect to your system and
  2. help your data more easily transform to other systems you may need, such as VAN.

Bluelink uses flexible and verbose data models. All fields are nullable and may be omitted if no data exists, unless explicitly stated otherwise. The structure may seem complicated for simple use cases, however it supports more complicated uses such as systems supporting multiple emails for a single individual.

By default, Bluelink writes data to partners in these formats. For partners with existing APIs (e.g., NGPVAN), we can easily and correctly convert between their format and ours. Therefore, we strongly encourage new partners to use these formats when building new APIs.

Person Model

JSON Example

This is an example of the data format as a JSON object with example values.

{
  "given_name":"Jane",
  "family_name":"Voter",
  "source":"Salesforce",
  "emails":[
    {
      "address":"jane@gmail.com"
    }
  ],
  "identifiers":[
    {
      "source":"VAN:MyVoters",
      "identifier":"101234567"
    },
    {
      "source":"SALESFORCE",
      "identifier":"0038A00000Z7XYZABC"
    },
    {
      "source":"COOLBLUE",
      "identifier":"321superblueid"
    }
  ],
  "addresses":[
    {
      "address_lines":[
        "123 4th St",
        "Apt 5"
      ],
      "city":"Seattle",
      "state":"WA",
      "postal_code":98103,
      "country":"US"
    }
  ],
  "details":{
    "favorite_color":"Blue",
    "notes":[
      {
        "date":"2020-01-19",
        "note":"Ask how the remodel is going."
      }
    ]
  },
  "created_date": 1643673600000,
  "modified_date": 1643992027000
}

Person Object

This is the top-level person object. There are nested fields that may be repeated.

Field Name Description Type
given_name First name / given name String
family_name Last name / family name String
salutation Preferred greeting / envelope name String
emails Email addresses Array of Email Objects
identifiers External identifiers, e.g., VAN:MyVoters ID Array of Identifier Objects
phones Phone numbers Array of Phone Objects
addresses Postal addresses Array of Address Objects
tags Simple tags that apply to the person, e.g., DONOR Array of Tag Objects
flags Similar to tags, but with more configuration options Array of Flag Objects
employer Name of the person’s employer String
employer_address Postal address of the person’s employer Address Object
occupation Occupation String
source Original source of this record. E.g., “Salesforce” String
birthdate ISO 8601 formatted birth date: YYYY-MM-DD String
geographies Geographical data, e.g., a congressional district Array of Geography Objects
scores Numeric scores, e.g., partisanship model Array of Score Objects
details Additional data Details Object
conversation Canvassing conversation Conversation Object
actions Array of actions associated with this person, such as canvassing conversations, donations, and events attended. The format will likely be specific to the type of data you collect. Contact us to discuss the data types and formats that are relevant to you. Custom object. Example: Conversation Object
created_date Timestamp when created in millis since epoch Integer
modified_date Timestamp of last update in millis since epoch Integer

Email Object

Field Name Description Type
id Email ID, if applicable String
id_source System this ID is from. If omitted, assumed to be Bluelink String
primary True if this is known to be the primary email Boolean
address Address, e.g., “user@gmail.com” String
type Type, e.g., “personal”, “work” String
status One of “Potential”, “Subscribed”, “Unsubscribed”, “Bouncing”, or “Spam Complaints” String

Geography Object

Field Name Description Type
id Geo Object ID, if applicable String
id_source System this ID is from. If omitted, assumed to be Bluelink String
layer_type Type of geography, e.g., congressional district String
name The name of this geographical district, e.g., “CD 3” String
state US state in ISO-3166-2 format String
source Original source of this geographical data String

Identifier Object

Field Name Description Type
source External system to which this ID belongs, e.g., “VAN:myCampaign”. See notes below String
identifier Case-sensitive ID in the external system String
details Additional data Details Object

Notes:

Bluelink has standardized strings for source. Using these will allow us to correctly understand the external IDs you add. source (unlike identifier) is case insensitive. We just like all caps.

Phone Object

Field Name Description Type
id Phone ID, if applicable String
id_source System this ID is from, e.g., “VAN:myCampaign” String
primary True if this is known to be the primary phone Boolean
number Number; may or may not include country code String
description Free-form description String
type Type, e.g., “Home”, “Work”, “Mobile” String
country ISO 3166-1 Alpha-2 country code String
sms_capable True if this number can accept SMS Boolean
do_not_call True if this number is on the US FCC Do Not Call Registry Boolean
details Additional data Details Object

Address Object

Field Name Description Type
id Address ID, if applicable String
id_source System this ID is from String
primary True if this is known to be the primary address Boolean
type Address type. One of “Home” (address where registered to vote), “Work”, “Mailing”, or “Other” String
venue Venue name, if relevant String
address_lines Street address including optional second line, e.g., unit number Array of Strings
city City or other locality String
state State in ISO 3166-2 format or other regional subdivision String
postal_code Zip or other postal code String
country ISO 3166-1 Alpha-2 country code String
status A value representing the status of the address. One of “Potential”, “Verified”, or “Bad” String

Score Object

Field Name Description Type
score Numeric score; meaning varies. Up to ~1e9 with 2 decimal places Float
score_type Type, e.g., “Partisanship model” String
source Original source of this score String

Note: “source” and “score_type” should be unique per person, effectively, an ID.

Tag Object

Field Name Description Type
tag Tag string; convention is either a simple string or a string with a prefix separated by a colon, e.g., “DONOR:GRASSROOTS” String

Flag Object

Field Name Description Type
tag Tag string; convention is either a simple string or a string with a prefix separated by a colon, e.g., “DONOR:GRASSROOTS” String
action What action to take with this flag in destination systems. One of “ADD”, “REMOVE”. Use flags instead of tags if you need the ability to remove tags in external systems. String

Details Object

A JSON object with whatever fields are necessary. Standardization is specific to the needs of the pipeline. Or just a nice place to put other data.

Conversation Model

Conversations, including canvass results and survey questions (Note: this model is a work in progress)

Conversation Object

Field Name Description Type
timestamp Time the contact occured Integer of milliseconds since epoch, or timestamp with timezone in ISO format (E.g. 2020-12-10T23:45:59+01:00)
contact_type How the person was contacted. E.g. Phone, Door Knock. String
contacted_phone (optional) Phone number that was contacted; may or may not include country code String
result Result of the contact. E.g. Canvassed, Not Home, Moved, Refused. String
survey_responses List of survey responses from this conversation Array of Survey Response Objects

Note: You can use the Bluelink mapping API or UI to provide a mapping between your system’s contact types/result types and the matching types in third parties you want to sync to.

Survey Response Object

Field Name Description Type
question_id ID of the question in your system. E.g. 12345 or “support”. String or Integer
question_name (optional) Name of the question in your system. E.g. “Candidate Support” String
question_text (optional) Full text of the question in your system. E.g. “Do you support our candidate?” String
response_id ID of the response in your system. E.g. 12345 or “true”. String or Integer
response_text (optional) Full text of the response in your system. E.g. “Yes” or “Strong Support”. String
external_question_ids (optional) IDs of matching questions in any third party systems. Alternatively, mappings between your system and others can be provided in the Bluelink mapping API or UI. Array of Identifier Objects
external_response_ids (optional) IDs of matching responses in any third party systems. Alternatively, mappings between your system and others can be provided in the Bluelink mapping API or UI. Array of Identifier Objects

API

Bluelink works most reliably when we can query the partner’s API. This is our preferred API to call which we can easily support in a reliable way.

Auth

The partner API should use basic HTTP auth with a secret API key or token as the password. The username will be used to identify the Bluelink client.

Example:

curl -u "bluelink-123:secret-api-key" \
"https://api.partner-service.org/v1/profile"

In this example the username is “bluelink-123”. “123” is the Bluelink Client ID.

The password, “secret-api-key” is the partner API key that was entered into Bluelink UI. Bluelink securely stores these keys using multiple layers of encryption.

Endpoints

Host and path prefix: https://api.<your_url>/v1
Host and path prefix for dev: https://api-dev.<your_url>/v1

A versioned path will make it simpler to change in the future, but is not required. We can work with whatever host and path you use.

If you have a development, staging or QA instance, we’d love to build against that! It is not required, but extremely helpful.

Profile

path: /profile
verb: GET

To validate the auth is correct, we’d like to hit an endpoint and get client metadata back. It should respond with status code 200 and the client name (or similar) if the credentials are valid.

Example:

curl -u "bluelink-123:secret-api-key" \
"https://api.partner-service.org/v1/profile"

Returns:
headers: “Content-type: application/json”
status code: 200
payload:

{"client_name": "Awesome Progressive Org"}

Invalid credentials should return the proper HTTP status code, e.g., 401 or 403.

Add or Update People

path: /people
verb: POST
headers: “Content-type: application/json”
payload: The JSON representation of the Person Object Data Model including nested fields

Bluelink will send person data to your service. Your service decides if it wants to add a new person, update an existing person, or reject the data outright. It would be entirely responsible for merging any fields to handle an update.

Important: To avoid an infinite update loop, the services should not set the updated timestamp on the record if no data has changed.

Example:

curl -X POST \
-u "bluelink-123:secret-api-key" \
-H "Content-type: application/json" \
-d '{"given_name": "Jane", "family_name": "Voter"}' "https://api.partner-service.org/v1/people"

On success, this can return either 204 with no content, or a 200 with whatever content you care to provide. Often, returning the newly created or newly updated data can be useful if Bluelink will do more with the data after updating your system.

Get People

path: /people
verb: GET
query params:

This will return a list of all people updated within the range provided in the query params. It need only provide the fields listed in the “fields” query param, but may include more.

Example:

curl -u "bluelink-123:secret-api-key" \
"https://api.partner-service.org/v1/people/?created_min=1608080540000&created_max=1608083540000&fields=emails,phones&page_id=0"

Returns:
headers: “Content-type: application/json”
status code: 200
payload:

{
  "people":[
    {
      "given_name":"Jane",
      "family_name": "Voter",
      "source": "CoolBlue",
      "emails": [
        {
          "address":"jane@gmail.com"
        },
        {
          "address":"jane@bluelink.org"
        }
      ],
      "phones": [
        {
          "number":"123-456-7890"
        }
      ]
    },
    {
      "given_name":"Annie",
      "family_name": "Ado",
      "source": "CoolBlue",
      "emails": [],
      "phones": []
    }
  ],
  "next_page_id": "1234"
}

Webhook

This is the simplest way to get data into Bluelink’s systems. You can post person and/or activity data to our API endpoint, and it will be saved in our system for use in your pipelines.

The API endpoint is https://api.bluelink.org/webhooks/.

Auth

Use basic HTTP auth to authenticate to our API. You can get a username and password at https://app.bluelink.org/bluelink-webhook-integration. If you are building an integration used by multiple clients, request one username/password per client.

Example:

curl -X POST \
-u "bluelink-123:secret-key" \
-H "Content-type: application/json" \
-d '{...webhook data...}'
"https://api.bluelink.org/webhooks/"

Usage

Each webhook request should represent a creation or update to one person’s data, and/or creation or update of one activity.

Webhook Request Object

The webhook request should contain the following fields:

Field Name Description Type
source (required) String to identify that the data came from your system. For example, your company name. We assume that all IDs (such as activity ID and topic ID) are IDs from this source system. String
person (required) The person to create or updated or the person associated with the activity. If the person is being created or updated, include a full person object with any fields you want added/changed. If you’re creating an activity for an existing person, we will use “person” to identify which person the activity is for. In that case you could include just an “identifier” for the person. A single request can create/update both a person and an activity. Person Object
activity (optional) The activity to create or update. Work with us to determine the best format to send your app’s activity data to Bluelink.

All subfields of person and activity are optional – send the data you have.

JSON Examples

Here is a sample webhook requests to submit a person to Bluelink.

Create/update a person

{ 
  "source": "COOLBLUE",
  "person":{
    "given_name":"Jane",
    "family_name":"Voter",
    "source":"Salesforce",
    "emails":[
      {
        "address":"jane@gmail.com"
      }
    ],
    "identifiers":[
      {
        "source":"VAN:MyVoters",
        "identifier":"98765432"
      },
      {
        "source":"SALESFORCE",
        "identifier":"0038A00000Z7XYZABC"
      },
      {
        "source":"COOLBLUE",
        "identifier":"321superblueid"
      }
    ],
    "addresses":[
      {
        "address_lines":[
          "123 4th St",
          "Apt 5"
        ],
        "city":"Seattle",
        "state":"WA",
        "postal_code":98103,
        "country":"US"
      }
    ],
    "details":{
      "favorite_color":"Blue",
      "notes":[
        {
          "date":"2020-01-19",
          "note":"Ask how the remodel is going."
        }
      ]
    }
  }
}
  1. Rate limits? Bluelink can support either a QPS limit or a max-in-flight limit. Just let us know!
  2. Do you have a dev/testing/staging/QA environment that Bluelink can build against?
  3. Do you have or need any data that doesn’t seem to fit nicely into the existing data model?

Per-client Info

To run a pipeline, we’ll need some info for the specific client.

  1. API Keys or OAuth must be set up in Bluelink via our UI.
    1. Both for your service and whatever system they want to connect to, e.g., VAN.

© 2022 Bluelink Data Systems, Inc

4301 50th Street NW, Suite 300, PMB 1011, Washington, DC 20016