Introduction
API Endpoint:
https://api.handwrite.io/v1/
Welcome to the Handwrite API!
You can use our API to send handwritten notes in an automated manner, using our REST endpoints.
We have language bindings in Shell and JavaScript, with Python and Ruby coming soon!
If you have questions or feedback please feel free to reach out to us here.
Getting Started
Handwrite uses API keys to allow access to the API. You must first sign up for a Handwrite account to obtain an API key. You will be charged per card based on the plan you choose.
Once you're logged in, you can create a key by going to the developer dashboard.
You'll notice when you create a key that it will start with test_hw
or live_hw
. Test keys will not count against your usage and can be used for testing purposes as you might have guessed!
An API key is expected to be included in all API requests to the server in a header that looks like the following:
Authorization: live_hw_sid92ldkasiie299dkw
Also, the content type must be Content-Type: application/json
Endpoints
Get Handwritings
The Request
curl --request GET \
--url https://api.handwrite.io/v1/handwriting \
--header 'authorization: test_hw_54838bde67e8e6255fa6' \
--header 'content-type: application/json'
const request = require("request");
const options = {
method: "GET",
url: "https://api.handwrite.io/v1/handwriting",
headers: {
"content-type": "application/json",
authorization: "test_hw_54838bde67e8e6255fa6"
}
};
request(options, (error, response, body) => {
if (error) throw new Error(error);
console.log(body);
});
The Response - An array of handwriting objects in JSON format
[
{
"_id": "5db6f0724cc1751452c5ae8e",
"name": "Jeremy",
"preview_url": "http://res.cloudinary.com/handwrite/image/upload/v1572270190/cards/wkwtnagsty79e0tlbiad.jpg"
},
{
"_id": "5db6f08c4cc1751452c5ae8f",
"name": "Tribeca",
"preview_url": "http://res.cloudinary.com/handwrite/image/upload/v1572270217/cards/hs92dvha3i5bvuhnboz7.jpg"
},
{
"_id": "5db6f0f14cc1751452c5ae90",
"name": "Terry",
"preview_url": "http://res.cloudinary.com/handwrite/image/upload/v1572270316/cards/bjocrcfhed6dwdadbtpv.jpg"
}
]
This will fetch handwriting options for your users to preview and choose from.
HTTP Request
POST https://api.handwrite.io/v1/handwriting
Get Stationery
The Request
curl --request GET \
--url https://api.handwrite.io/v1/stationery \
--header 'authorization: test_hw_54838bde67e8e6255fa6' \
--header 'content-type: application/json'
const request = require("request");
const options = {
method: "GET",
url: "https://api.handwrite.io/v1/stationery",
headers: {
"content-type": "application/json",
authorization: "test_hw_54838bde67e8e6255fa6"
}
};
request(options, (error, response, body) => {
if (error) throw new Error(error);
console.log(body);
});
The Response - An array of stationery objects in JSON format
[
{
"_id": "5db6f1854cc1751452c5ae93",
"name": "Classic White",
"preview_url": "http://res.cloudinary.com/handwrite/image/upload/v1572270464/cards/yflijfai9wm38czthluk.jpg"
},
{
"_id": "5db6f19b4cc1751452c5ae95",
"name": "Classic Navy",
"preview_url": "http://res.cloudinary.com/handwrite/image/upload/v1572270484/cards/afwlmwfs4dkh5s6xt5uc.jpg"
},
{
"_id": "5db6f1b74cc1751452c5ae96",
"name": "Hello",
"preview_url": "http://res.cloudinary.com/handwrite/image/upload/v1572270512/cards/mb9fhgwgnkdxiqtr9k0i.jpg"
}
]
This will fetch any custom stationery you have uploaded as well as the publicly available options we provide.
HTTP Request
GET https://api.handwrite.io/v1/stationery
Send a Letter
The Request
curl --request POST \
--url https://api.handwrite.io/v1/send \
--header 'authorization: test_hw_54838bde67e8e6255fa6' \
--header 'content-type: application/json' \
--data '{
"message": "Hey dude,\nIt was great meeting you last week at the party. We'\''d love to have you back at the next one!\n\nBest,\n-Jackie",
"handwriting": "5db6f0724cc1751452c5ae8e",
"card": "5db6f0724cc1751452c5ae8e",
"recipients": [
{
"firstName": "The",
"lastName": "Dude",
"company": "Unemployed",
"street1": "25 Main Street",
"city": "Los Angeles",
"state": "CA",
"zip": "90210"
}
],
"from": {
"firstName": "Jackie",
"lastName": "Treehorn",
"street1": "1 Random Street",
"street2": "Apt 33A",
"city": "Malibu",
"state": "CA",
"zip": "90263"
}
}'
const request = require("request");
const options = {
method: "POST",
url: "https://api.handwrite.io/v1/send",
headers: {
"content-type": "application/json",
authorization: "test_hw_54838bde67e8e6255fa6"
},
body: {
message:
"Hey dude,\nIt was great meeting you last week at the party. We'd love to have you back at the next one!\n\nBest,\n-Jackie",
handwriting: "5db6f0724cc1751452c5ae8e",
card: "5db6f0724cc1751452c5ae8e",
recipients: [
{
firstName: "The",
lastName: "Dude",
company: "Unemployed",
street1: "25 Main Street",
city: "Los Angeles",
state: "CA",
zip: "90210"
}
],
from: {
firstName: "Jackie",
lastName: "Treehorn",
street1: "1 Random Street",
street2: "Apt 33A",
city: "Malibu",
state: "CA",
zip: "90263"
}
},
json: true
};
request(options, (error, response, body) => {
if (error) throw new Error(error);
console.log(body);
});
The Response - An array of
orders
in JSON format
[
{
"_id": "5dba45e8f2b173cb5dff0300",
"message": "Hey dude,\nIt was great meeting you last week at the party. We'd love to have you back at the next one!\n\nBest,\n-Jackie",
"to": {
"firstName": "The",
"lastName": "Dude",
"company": "Unemployed",
"street1": "25 Main Street",
"city": "Los Angeles",
"state": "CA",
"zip": "90210"
},
"from": {
"firstName": "Jackie",
"lastName": "Treehorn",
"street1": "1 Random Street",
"street2": "Apt 33A",
"city": "Malibu",
"state": "CA",
"zip": "90263"
},
"status": "processing",
"handwriting": "5db6f0724cc1751452c5ae8e",
"card": "5db6f0724cc1751452c5ae8e",
"createdAt": "2019-10-31T02:24:40.648Z"
}
]
Send a letter to up to 10 recipients at once.
HTTP Request
POST https://api.handwrite.io/v1/send
Request parameters
Parameter | Type | Description |
---|---|---|
message required | String | This is the body of the letter. Maximum of 320 characters. |
card required | String | ID of the stationery/card you want. This will also determine whether it is the front or back of card. |
handwriting required | String | ID of the handwriting you want to use. |
recipients required | Array | List of recipient objects. Must have at least one, but can be up to 10. |
recipients[n].firstName | String | Recipient first name |
recipients[n].lastName | String | Recipient last name |
recipients[n].company | String | Recipient company. If this is included, company will be on the first line, with attention to on the second |
recipients[n].street1 required | String | First line of the address, e.g. 555 Terrance Street |
recipients[n].street2 | String | Second line of the address, e.g. Apt. 201 |
recipients[n].city required | String | |
recipients[n].state required | String | *Must be the capitalized two digit abbreviation, e.g. AL instead of Alabama |
recipients[n].zip required | String | Must be exactly 5 characters |
from | Object | Return address. (optional) |
from.firstName | String | |
from.lastName | String | |
from.street1 | String | |
from.street2 | String | |
from.city | String | |
from.state | String | |
from.zip | String |
Batch Mode
This endpoint also accepts an array, where each element is an object in the above format. The format of the response is the same as for sending a single message. A maximum of 1,000 individual orders (1 message x 1 recipient) may be submitted at a time.
Get an Order
The Request
curl --request GET \
--url https://api.handwrite.io/v1/order/5f44086e69217700172ac110 \
--header 'authorization: test_hw_54838bde67e8e6255fa6'
var request = require("request");
var options = {
method: "GET",
url: "https://api.handwrite.io/v1/order/5f44086e69217700172ac110",
headers: { authorization: "test_hw_54838bde67e8e6255fa6" }
};
request(options, function(error, response, body) {
if (error) throw new Error(error);
console.log(body);
});
The Response - An object in JSON format
{
"_id": "5f44086e69217700172ac110",
"message": "Hey there, hope all is well!",
"status": "complete",
"handwriting": "5dc30652bc08d20016f1ec33",
"card": "5f33fde848cc140017f0364a",
"createdAt": "2020-08-24T18:35:26.686Z",
"environment": "live",
"to": {
"firstName": "Jamie",
"lastName": "Stockton",
"company": "Stockton Lumber",
"street1": "8284 Random Road",
"city": "Sarasota",
"state": "FL",
"zip": "34240"
},
"from": {
"firstName": "Terrance",
"lastName": "McGhee",
"street1": "293 Hungerford Drive",
"street2": "",
"city": "Rockville",
"state": "MD",
"zip": "20850"
},
"proofs": [
{
"job_type": "card",
"image_url": "https://s3.us-east-2.amazonaws.com/any-random-image.jpg"
},
{
"job_type": "envelope",
"image_url": "https://s3.us-east-2.amazonaws.com/another-random-image.jpg"
}
]
}
This will allow you to fetch an order you've already placed whether it was from the web app or the API.
This will allow you to get the status of your order, which will be one of:
processing
written
(but not yet delivered)complete
(has been mailed)problem
(rare, technical issue on our end which we will resolve)cancelled
(also rare as we do not allow cancellations typically)
It will also allow you to access your proofs (visual images of your letter front and envelope) if the letter has been completed.
HTTP Request
GET https://api.handwrite.io/v1/order/:orderId
Rate Limiting
The Handwrite API is rate limited to prevent abuse that would degrade our ability to maintain consistent API performance for all users.
By default, each API key is rate limited at 60 requests per minute. If your requests are being rate limited, HTTP response code 429
will be returned with an rate_limit_exceeded
error.
In your response headers, you should see information pertaining to your limits at any given time.
Header | Description |
---|---|
X-RateLimit-Limit |
The maximum number of requests that the consumer is permitted to make per minute. |
X-RateLimit-Remaining |
The number of requests remaining in the current rate limit window. |
X-RateLimit-Reset |
The time at which the current rate limit window resets. |
Response Codes
Handwrite uses conventional HTTP response codes to indicate the success or failure of an API request. In general: Codes in the 2xx
range indicate success. Codes in the 4xx
range indicate an error that failed given the information provided (e.g., a required parameter was omitted or was the incorrect type, etc.). Codes in the 5xx
range indicate an error with our servers.
The Handwrite API uses the following response codes:
Error Code | Meaning |
---|---|
200 | Success. Everything worked as expected |
400 | Bad Request -- Your request is invalid. |
401 | Unauthorized -- Your API key is wrong. |
404 | Not Found -- The specified resource could not be found. |
429 | Too Many Requests -- You're requesting too much! Slow down! |
500 | Internal Server Error -- We had a problem with our server. Try again later. |
503 | Service Unavailable -- We're temporarily offline for maintenance. Please try again later. |
Third Party Packages
Many thanks to Tim Pile who created a library for using Elixir with the Handwrite API.