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) |
Content
get
Endpoint: /api/cnd/get
The content endpoint allows you to get access to blog posts and galleries. The following parmeters are accepted.
id | Blog id (required) |
detail | Default: 0 (optional) 0 == abridged data, 1 == Gets the full unabridged body detail |
filters |
Array (optional) |
filters[limit] | Default: 200 (max) 0 == None, 1 == Card, 2 == Invoice or bill to pay later |
filters[page] | Default: 1 For pagination of data sets in blocks the size defined by limit above |
filters[date_range_published] | Date format: array Filter based on the date the content item was published. Y-m-d hh:mm:ss (e.g., [2020-08-23 00:00:00, 2020-08-30 23:59:59] ) array[0] == start_date, array[1] == end_date |
filters[date_range_added] | Date format: array Filter based on the date the content item was added. Y-m-d hh:mm:ss (e.g., [2020-08-23 00:00:00, 2020-08-30 23:59:59] ) array[0] == start_date, array[1] == end_date |
example:
GET /api/cnd/get?client_id=your_client_id&api_key=your_secret_api_key&id=1&detail=1&filters[limit]=1&filters[date_range_published][0]=2021-01-01 00:00:00&filters[date_range_published][1]=2021-02-01 23:59:59
JSON Response
{
"success":true,
"results":
[{
"id": "94422",
"version_id": "313919",
"title": "Pigeon Paywall API",
"subtitle": null,
"excerpt": "Pigeon is a solution for your paid access needs.",
"body": "<div>All the power you need in one solution to better monetize your content.</div>",
"author": "CJ",
"publish_date": {
"date": "2021-02-01 11:19:01.000000",
"timezone_type": 3,
"timezone": "America\/Indiana\/Indianapolis"
},
"type": "1",
"comment_count": "0",
"order_index": "0",
"file_hash": null,
"enabled": "1",
"image_node_id": "124953",
"default_image_id": "813163",
"default_image_actual_width": "254",
"default_image_actual_height": "300",
"default_image_size": "16147",
"default_image_credit": null,
"default_image_caption": "",
"default_image_added": "1612203621",
"extension": "jpg",
"default_image": "https:\/\/url-to-cdn.com\/89f09e44865a9f83b6cb6bf0a1446fc3.jpg",
"permalink": "https:\/\/pigeonpay.com\/"
}]
}
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.
- You need to authenticate users via native mobile apps.
- You want to manage your own session handling.
- 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/search?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) |
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
}
]
}
]
}