API

The Pigeon API is based on REST. JSON is returned by all API responses, including errors. Currently we only have one official language-specific API library. Let us know if you have developed a library in another language. We can link to other community supported languages.

Official Libraries

Authentication

Authenticate your account by including your secret api key in API requests. Contact Pigeon support to generate your keys. Dashboard management of API keys is coming soon.

All API requests must be made over HTTPS. Calls made over plain HTTP will fail. API requests without authentication will also fail.

HTTP Status Codes

200 - OK

Everything worked as expected.

401 - Unauthorized

No valid API key provided.

500, 502, 503, 504 - Server Errors

Server errors (very rare)

Customer

Endpoint: /api/customer

find

Use the following example get a customer response back if you have the customer id.

GET /api/customer?client_id=your_client_id&api_key=your_secret_api_key&id=6e4a8f33oreg8jd0ln1

JSON Response

{
  "success": true,
  "customer": {
    "email": "nick@sabramedia.com",
    "display_name": "Nick Johnson",
    "sex": "",
    "dob": "",
    "first_name": "Nick",
    "last_name": "Johnson",
    "group_type": false,
    "id": "6e4a8f33oreg8jd0ln1",
    "parent": [],
    "access": true,
    "plans": [
      {
        "access_id": 3,
        "access_tag": "default",
        "name": "Print + Digital Bundle Monthly $7.00 subscription",
        "start": "2018-05-01",
        "type": 1
      }
    ]
  }
}

login

Endpoint: /api/customer/login

Reasons to use this endpoint.

  1. You need to authenticate users via native mobile apps.
  2. You want to manage your own session handling.
  3. Advanced integration of our user data with your internal user profile database.

The easiest way to manage login sessions is to use the JavaScript plugin or SDK tools. Both these handle the cookie-to-server session relationship automatically for you. Or you can implement your own authentication and session handling by using this customer login endpoint.

In addition to the API authentication credentials you need to pass the user's email and password to this endpoint to determine if they are a registered user. If there is a match, then the customer details are returned. At this point you can handle your sessions locally and use the customer id to grab user details to determine user access status. If the user credentials don't match, the response status will fail and send back a reason message.

Flow Example

Post the login request to authenticate the customer.

CURL -X POST {pigeon_domain_here}/api/customer/login -H "Content-Type: application/json" -d '{"client_id":"your_client_id","api_key":"your_api_key","email":"pigeon@sabramedia.com","password":"customer_password"}'

If success, then store the customer id in a local session. Notice the customer.id in the example JSON response below:

{
  "success": true,
  "customer": {
    "id": "6e4a8focoe8jd0ln1",
    "email": "nick@sabramedia.com",
    "display_name": "Nick Johnson",
    "first_name": "Nick",
    "last_name": "Johnson",
    "group_type": true,
    "parent": [],
    "access": true,
    "plans": [
      {
        "access_id": 1,
        "term_id": 3,
        "access_tag": "default",
        "name": "Premium Digital Access Monthly",
        "start": "2018-08-23",
        "type": 1
      }
    ],
    "password_set_url": "http:\/\/staging.sabramedia.com\/password-reset?h=9e2245d765f52af89e2d50cd77621dcb",
    "sessions": 2,
    "sso_link": "{pigeon_domain_here}?psso=Y3VzdG9tZXJfaWQ9NmU0YThmb2NvZThqZDBsbjEmcmQ9aHR0cCUzQSUyRiUyRnN0YWdpbmcuc2FicmFtZWRpYS5jb20=&sig=7a0994e3c224c7cb5565193db04d1d2c6c7c02067287b31066b588159658de62"
  },
  "session": {
    "id": 79994,
    "hash": "d3659496ec0fb6acadb089091ea51470",
    "fingerprint": null,
    "set_method": 2,
    "ip_address": "192.0.0.1",
    "timeout": 1548608580,
    "domain": "sabramedia.com"
  },
  "token": "d3659496ec0fb6acadb089091ea51470"
}

Query our servers with their customer.id using the find verb (example below). Do this on subsequent calls to your service. This will keep continuity with the customer's access status in our database.

GET /api/customer?client_id=your_client_id&api_key=your_secret_api_key&id=6e4a8f33oreg8jd0ln1

No paywall rules are factored in the access check. This is fine. In most cases you will just check the access boolean (true or false). If true then they have an enabled account (customer.access == true).  You can develop additional access logic by iterating over the plan(s) to decide what content a person has access to based on their plan. If customer access is false, then you will need to link them to their user account in a browser via the customer's returned customer.sso_link. From there they can mange their access.

When your local session expires or they logout, then you would start back over from beginning.

search

Endpoint: /api/customer/search

Use this to find customers based on various parameters. Returns an array of results.

search Searches customer name, company name, email, phone, postal code, and third party id (required)

GET Example

GET /api/customer?client_id=your_client_id&api_key=your_secret_api_key&search=pigeon@sabramedia.com

JSON Response

{
  "success": true,
  "results": [
    {
      "id": "xenblda7vh6l6qrs679",
      "display_name": "",
      "full_name": "",
      "email": "pigeon@sabramedia.com",
      "status": "0",
      "authorized": "0",
      "addresses": []
    }
  ]
}

create

The data parameter needs to be a JSON encoded object. Most libraries handle HTTP POST encoding automatically. But keep in mind that the JSON object needs to be encoded via this media type application/x-www-form-urlencoded. This simulates posting a form over the Internet.

JSON Encoded data parameters

display_name Customer's full name (optional)
email Customer's email (required)
password Customer's password (optional)
send_notice Trigger the welcome notice. Default is TRUE. (optional)
internal_id ID of third party system. Can be used to relate the Pigeon customer to third party id. Is visible on profile tab and exports.

CURL POST example

CURL -X POST {pigeon_domain_here}/api/customer -H "Content-Type: application/json" -d '{"client_id":"your_client_id","api_key":"your_api_key","display_name":"Nick Johnson","email":"pigeon@sabramedia.com"}'

JSON Response

{
  "success": true,
  "customer": {
    "email": "pigeon@sabramedia.com",
    "display_name": "Nick Johnson",
    "sex": null,
    "dob": null,
    "first_name": "",
    "last_name": null,
    "group_type": false,
    "id": "xenblda7vh6l6qrs679",
    "parent": [],
    "access": false,
    "plans": null
  }
}

add plan

Endpoint: /api/customer/add_plan

Add a plan based on a known plan number. Currently we don't accept payment method details via the API. When the payment method is set to card the customer is prompted to activate their account with their payment details.

customer_id Customer's id returned in get or create requests (required)
plan_number A known plan number (required)
plan_type Default: 1 (optional)
1 == subscription, 2 == subscription trial
payment_method Default: 1 (optional)
0 == None, 1 == Card, 2 == Invoice or bill to pay later
send_notice Default: TRUE (optional)
If true then the order notice is sent. If false, then it's not sent.

CURL POST example (before url encoding)

CURL -X POST {pigeon_domain_here}/api/customer/add_plan -H "Content-Type: application/json" -d '{"client_id":"your_client_id","api_key":"your_api_key","customer_id":"xenblda7vh6l6qrs679","plan_number":"12345","plan_type":"1","payment_method":"1"}'

JSON Response

{
  "success": true,
  "customer": {
    "email": "nick@sabramedia.com",
    "display_name": "Nick Johnson",
    "sex": "",
    "dob": "",
    "first_name": "Nick",
    "last_name": "Johnson",
    "group_type": false,
    "id": "6e4a8f33oreg8jd0ln1",
    "parent": [],
    "access": true,
    "plans": [
      {
        "access_id": 3,
        "access_tag": "default",
        "name": "Print + Digital Bundle Monthly $7.00 subscription",
        "start": "2018-05-01",
        "type": 1
      }
    ]
  }
}

Plan

Coming soon.

Subscription

Endpoint: /api/subscription

Summary

Endpoint: /api/subscription/summary

GET example

GET /api/subscription/summary?client_id=your_client_id&api_key=your_secret_api_key

date_range Default: Withing the last month of yesterday (optional)
Date format: Y-m-d (e.g., 2018-08-23)
array[0] == start_date, array[1] == end_date

GET Example with optional date range filter:

GET /api/subscription/summary?client_id=your_client_id&api_key=your_secret_api_key&date_range[]=2018-04-01&date_range[]=2018-04-28

JSON Response

The total is overall total number of current subscribers. The added and canceled numbers are the change made during the given date range. The plan "terms" are the term breakdown of each plan.

{
  "success": true,
  "date_range": {
    "start": {
      "date": "2018-04-01 00:00:00",
      "timezone_type": 3,
      "timezone": "America\/Chicago"
    },
    "end": {
      "date": "2018-04-28 23:59:59",
      "timezone_type": 3,
      "timezone": "America\/Chicago"
    }
  },
  "results": [
    {
      "product_id": "1",
      "combo_id": null,
      "product_name": "Digital Only",
      "total": "2134",
      "added": "5",
      "canceled": "1",
      "terms": [
        {
          "item_id": "2",
          "product_id": "1",
          "combo_id": null,
          "product_name": "Digital Only",
          "item_name": "Annual",
          "item_number": "v5yyu",
          "total": "73",
          "added": 1,
          "canceled": 0
        },
        {
          "item_id": "1",
          "product_id": "1",
          "combo_id": null,
          "product_name": "Digital Only",
          "item_name": "Monthly",
          "item_number": "105lu",
          "total": "2061",
          "added": 4,
          "canceled": 1
        }
      ]
    },
    {
      "product_id": "2",
      "combo_id": null,
      "product_name": "Digital + Print",
      "total": "61",
      "added": "4",
      "canceled": "0",
      "terms": [
        {
          "item_id": "4",
          "product_id": "2",
          "combo_id": null,
          "product_name": "Digital + Print",
          "item_name": "Annual",
          "item_number": "tryfb",
          "total": "32",
          "added": 2,
          "canceled": 0
        },
        {
          "item_id": "3",
          "product_id": "2",
          "combo_id": null,
          "product_name": "Digital + Print",
          "item_name": "Monthly",
          "item_number": "qwdo4",
          "total": "29",
          "added": 2,
          "canceled": 0
        }
      ]
    }
  ]
}