Send a PDF
On this page
API docs
Get started
Guides
Endpoints
Account
Templates
Sending
Documents
Files
OpenAPI
Guides
Send a PDF with the API
Send a PDF your system produced, such as a quote or an order form, without setting up a template first. You upload the file, then say who signs and where each field goes.
You can place fields in two ways: by their position on the page, which this guide covers, or with text markers you write into the PDF.
The three steps
Start the upload
Reserve an upload with the file's name and its exact size in bytes. A PDF can be up to 20 MB and 500 pages. Your key needs the
documents:sendscope.Start the uploadcurl -X POST "https://www.inkly.so/api/v1/uploads" \ -H "Authorization: Bearer $INKLY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "file_name": "consulting-agreement.pdf", "byte_size": 482133 }'Upload the file
Send the bytes to
upload_urlwithPUTbeforeexpires_at, one hour later. This request goes to the file store, so leave out your API key.Upload the filecurl -X PUT "$UPLOAD_URL" \ -H "Content-Type: application/pdf" \ --data-binary @consulting-agreement.pdfSend the document
Pass
upload_id, the recipients, and the fields, with an Idempotency-Key so a retry never sends twice. Each person gets aref, your own short name for them, and each field points at a signer'sref.Send the documentcurl -X POST "https://www.inkly.so/api/v1/documents/from-upload" \ -H "Authorization: Bearer $INKLY_API_KEY" \ -H "Idempotency-Key: crm-8841-send-1" \ -H "Content-Type: application/json" \ -d '{ "upload_id": "d06b8e24-9f13-4c7a-a5d8-3e1c7f2b6049", "title": "Consulting agreement", "external_id": "crm-8841", "signing_order": "parallel", "recipients": [ { "ref": "client", "name": "Alex Morgan", "email": "alex@example.com", "delivery": "link" }, { "ref": "finance", "role": "cc", "name": "Sam Lee", "email": "sam@example.com" } ], "fields": [ { "recipient": "client", "type": "signature", "page": 1, "x": 0.1, "y": 0.8, "width": 0.35, "height": 0.06 }, { "recipient": "client", "type": "date", "page": 1, "x": 0.55, "y": 0.8, "width": 0.2, "height": 0.04 } ] }'
If you send before the file has arrived you get upload_incomplete, and after the hour is up you get upload_expired. An upload sends one document: using it again returns upload_already_used. The reference lists every option and error.
Placing fields by coordinates
Each field has a page, starting at 1, and four numbers that say where it sits on that page. They are fractions of the page rather than points or pixels, measured from the top-left corner:
xis the distance from the left edge to the field's left edge, as a fraction of the page width.yis the distance from the top edge to the field's top edge, as a fraction of the page height.widthandheightare the field's size, as fractions of the page width and height.
So x: 0.5 starts a field halfway across the page, whatever size the page is.
Converting from PDF points
PDF tools usually report positions in points (1/72 of an inch), measured from the bottom-left corner, with the field's bottom-left corner as its position. Divide by the page size, and flip the vertical position:
x = left / page_width
y = (page_height - bottom - height) / page_height
width = field_width / page_width
height = field_height / page_heightFor example, a 180 × 40 pt signature field whose bottom-left corner is at (72, 120) pt lands here on the two most common page sizes:
| Value | US Letter, 612 × 792 pt | A4, 595 × 842 pt |
|---|---|---|
| x | 0.1176 | 0.1210 |
| y | 0.7980 | 0.8100 |
| width | 0.2941 | 0.3025 |
| height | 0.0505 | 0.0475 |
{
"recipient": "client",
"type": "signature",
"page": 1,
"x": 0.1176,
"y": 0.7980,
"width": 0.2941,
"height": 0.0505
}Pages in one PDF can differ in size, so use the size of the page each field is on.
Rotated and cropped pages
Fractions always describe the page as a person sees it in a PDF viewer, which can differ from the numbers inside the file:
- Rotated pages. On a page rotated 90° or 270°, the top-left corner is the one at the top left once the page is upright, and its width and height swap. A tool that reports positions on the unrotated page will not match.
- Cropped pages. The page is its visible area: the CropBox, where it fits inside the MediaBox. A field at
x: 0starts at the visible edge. If your tool reports positions on the full MediaBox, subtract the crop's left and bottom offsets, and use the crop's size as the page size.
Staying on the page
x + width and y + height can be at most 1, and page can be at most the number of pages. A field that goes past any of these is refused with field_out_of_bounds, and param says which field. A single value outside 0 to 1 returns invalid_request instead. A field right at the page edge can go over through rounding, so round its values down.
A refused send creates no document, and after one of these errors the upload stays usable until it expires. Fix the request and send it with a new Idempotency-Key, because reusing a key with a different body can return idempotency_key_reused.
The exceptions are invalid_pdf and pdf_too_many_pages, which end the upload. Fix the PDF and start a new upload.
Finding the positions
If your system generates the PDF, it already knows where it drew the signature line, so convert that position. Otherwise, open the PDF in a tool that shows the cursor position in points, and note the corners of each field.
Then send it to your own email address and check that each field lands where you expect before you send to anyone else.
Field types and sizes
These sizes, in points, are a good starting point. A signature scales to fit its field without stretching.
signature180 × 40initials72 × 28name, email, text180 × 20date, number110 × 20checkbox14 × 14
dropdownalso needsoptions: 2 to 50 choices.- Make text fields wide enough for the longest answer. Text gets smaller to fit, down to 6 points. Past that, a prefilled value is cut off at the field's edge, and an answer a signer types wraps onto new lines below the field, over anything printed there.
- Fields are required unless you set
requiredto false. - A
textfield can be prefilled: setprefilledto true, with alabeland aprefill_value, and it locks with your value. Prefilled fields with the same label must have the same value, or the send returns prefilled_value_conflict. - Every signer needs at least one field to fill in themselves, and a prefilled field does not count. Otherwise the send returns signer_has_no_fields.
- A document can have up to 500 fields and 25 recipients, at most 5 of them CCs.
This request uses every field type. The client gets a prefilled project name they cannot change, their name and email, a dropdown, a number, an optional PO number, a checkbox, their initials, a signature, and a date. Then the agency signs, because signing_order is sequential, and the CC gets a copy once both have signed.
curl -X POST "https://www.inkly.so/api/v1/documents/from-upload" \
-H "Authorization: Bearer $INKLY_API_KEY" \
-H "Idempotency-Key: crm-8841-send-1" \
-H "Content-Type: application/json" \
-d '{
"upload_id": "d06b8e24-9f13-4c7a-a5d8-3e1c7f2b6049",
"title": "Consulting agreement",
"signing_order": "sequential",
"recipients": [
{
"ref": "client",
"name": "Alex Morgan",
"email": "alex@example.com"
},
{
"ref": "agency",
"name": "Jordan Park",
"email": "jordan@agency.example"
},
{
"ref": "finance",
"role": "cc",
"name": "Sam Lee",
"email": "sam@example.com"
}
],
"fields": [
{
"recipient": "client",
"type": "text",
"page": 1,
"x": 0.1,
"y": 0.2,
"width": 0.4,
"height": 0.03,
"label": "Project",
"prefilled": true,
"prefill_value": "Website redesign"
},
{
"recipient": "client",
"type": "name",
"page": 1,
"x": 0.1,
"y": 0.3,
"width": 0.3,
"height": 0.03
},
{
"recipient": "client",
"type": "email",
"page": 1,
"x": 0.5,
"y": 0.3,
"width": 0.3,
"height": 0.03
},
{
"recipient": "client",
"type": "dropdown",
"page": 1,
"x": 0.1,
"y": 0.4,
"width": 0.3,
"height": 0.03,
"label": "Plan",
"options": [
"Starter",
"Growth",
"Scale"
]
},
{
"recipient": "client",
"type": "number",
"page": 1,
"x": 0.5,
"y": 0.4,
"width": 0.18,
"height": 0.03,
"label": "Seats"
},
{
"recipient": "client",
"type": "text",
"page": 1,
"x": 0.1,
"y": 0.5,
"width": 0.3,
"height": 0.03,
"label": "PO number",
"required": false
},
{
"recipient": "client",
"type": "checkbox",
"page": 1,
"x": 0.1,
"y": 0.6,
"width": 0.025,
"height": 0.018,
"label": "I agree to the terms"
},
{
"recipient": "client",
"type": "initials",
"page": 1,
"x": 0.85,
"y": 0.92,
"width": 0.12,
"height": 0.035
},
{
"recipient": "client",
"type": "signature",
"page": 2,
"x": 0.1,
"y": 0.8,
"width": 0.3,
"height": 0.05
},
{
"recipient": "client",
"type": "date",
"page": 2,
"x": 0.45,
"y": 0.82,
"width": 0.18,
"height": 0.03
},
{
"recipient": "agency",
"type": "signature",
"page": 2,
"x": 0.1,
"y": 0.9,
"width": 0.3,
"height": 0.05
}
]
}'When to use field markers
If you control the text of the PDF, field markers are often easier: write something like {{signature;signer=client}} where the field belongs, and Inkly finds it. A field then stays in the right place when the text around it moves.
Use coordinates for scans, which have no text to find, for PDFs exported from Microsoft Word, which markers refuse, and for any PDF you cannot edit.