Print content on a reader
Print content on a reader's embedded printer.
With Stripe Terminal, you can print custom content – such as receipts, tickets, or other documents – directly on a reader’s embedded printer. This feature is only available for the Verifone V660p reader, which is equipped with an embedded thermal printer capable of printing in black and white.
Generating print content
Your integration is responsible for generating the content to print as an image file. Images are the only format that we support for printing. After you have an image, convert it to the native image representation for your integration type (for example, an image file for server-driven or a Bitmap for Android).
Content guidelines
For best results, we recommend images that are:
- At most 384 pixels wide
- Black and white (no colour)
- Designed so that the thinnest part of any character or line is at least 2 pixels wide, to make sure printing is clear and legible
Note
You’re legally responsible for any content submitted to Stripe for printing using readers, and must make sure your receipts comply with applicable law. You’re prohibited from printing receipts containing materials that (i) infringe on the rights of any third party, (ii) violate applicable law, or (iii) contain objectionable, offensive, deceptive, inaccurate, or harmful content.
Printing content
To print content on a reader, call the print_content endpoint. Send a multipart/form-data body with your PNG or the related setting image file. The uploaded file must not exceed 100 KB.
Command Line
cURL
Note
This endpoint lives on the files.stripe.com subdomain instead of api.stripe.com.
Handle the print result
The print_content endpoint responds with an updated Reader object when the print command successfully sends to the reader. See the following example response:
{
"id": "tmr_FDOt2wlRZEdpd7",
"object": "terminal.reader",
"action": {
"failure_code": null,
"failure_message": null,
"print_content": {
"image": {
"created_at": 1748971913,
"filename": "image.png",
"size": 15864,
"type": "png"
},
"type": "image"
},
"status": "in_progress",
"type": "print_content"
},
"device_sw_version": "2.37.2.0",
"device_type": "verifone_v660p",
"ip_address": "0.0.0.0",
"label": "Blue Rabbit",
"last_seen_at": 1695166525506,
"livemode": false,
"location": "tml_FDOtHwxAAdIJOh",
"metadata": {},
"serial_number": "452-406-028",
"status": "online"
}
The action object shows that a print_content action is in_progress. The print_content object contains information about the image being printed.
The endpoint immediately responds to a validation error (for example, file too large or unsupported format) with a 400 status code and a body, such as the following:
{
"error": {
"message": "The file you uploaded was too large. Please upload a file smaller than 100 KB",
"param": "image",
"request_log_url": "https://dashboard.stripe.com/...",
"type": "invalid_request_error"
}
}
Webhook events
When the print operation completes (either successfully or with an error), Stripe sends a request to your webhook endpoint.
Stripe sends two webhook events to notify your back end of the print command’s result:
- terminal. reader. action _ succeeded : The print command succeeded.
- terminal. reader. action _ failed : The print command failed. For example, the printer ran out of paper or jammed during printing.
The webhook payload contains an event object where data.object is an updated Reader object.
The terminal.reader.action_succeeded event
For a terminal.reader.action_succeeded event, the data object looks like the following:
{
"object": {
"id": "tmr_FDOt2wlRZEdpd7",
"object": "terminal.reader",
"action": {
"failure_code": null,
"failure_message": null,
"print_content": {
"image": {
"created_at": 1748971913,
"filename": "image.png",
"size": 15864,
"type": "png"
},
"type": "image"
},
"status": "succeeded",
"type": "print_content"
},
"device_deploy_group": "",
"device_sw_version": "2.34.0.0",
"device_type": "verifone_v660p",
"ip_address": "10.244.38.93",
"label": "test",
"livemode": false,
"location": "tml_GEPlmAJnZUoXI6",
"metadata": {},
"serial_number": "452-406-028",
"software": null,
"status": "online"
},
"previous_attributes": null
}
The action.status is set to succeeded. The action.failure_code and action.failure_message are both null in this case.
The terminal.reader.action_failed event
For a terminal.reader.action_failed event, the data object looks like the following:
{
"object": {
"id": "tmr_FDOt2wlRZEdpd7",
"object": "terminal.reader",
"action": {
"failure_code": "reason_out_of_paper",
"failure_message": "The printer is out of paper.",
"print_content": {
"image": {
"created_at": 1748971913,
"filename": "image.png",
"size": 15864,
"type": "png"
},
"type": "image"
},
"status": "failed",
"type": "print_content"
},
"device_deploy_group": "",
"device_sw_version": "2.34.0.0",
"device_type": "verifone_v660p",
"ip_address": "10.244.35.107",
"label": "test",
"livemode": false,
"location": "tml_GEPlmAJnZUoXI6",
"metadata": {},
"serial_number": "452-406-028",
"software": null,
"status": "online"
},
"previous_attributes": null
}
The action.status is set to failed. The failure_code field contains the reason for the failure, and the failure_message field contains a human readable message describing the failure.
The possible values for the failure_code field are:
| Error Description | Failure Code |
|---|---|
| The printer is currently printing. | reason_busy |
| The printer paper roll is jammed. | reason_paperjam |
| The printer is out of paper. | reason_out_of_paper |
| The printer’s cover or head assembly is open. | reason_cover_open |
| The reader doesn’t have an embedded printer. | reason_absent |
| The reader has an embedded printer, but it’s currently unavailable. | reason_unavailable |
| The reader’s battery is too low to print. Charge the reader and try again. | reason_low_battery |
| General failure to print (catch all for any other error). | reason_generic_failure |
If the reader doesn’t support printing, the print_content request immediately returns a printer_not_supported error. Because the request doesn’t create an action, Stripe doesn’t send a terminal.reader.action_failed event.
