Packets API
A packet is a bundle of forms and documents that you send to one or more people to complete as a single, trackable unit. You design a reusable packet template once, then start individual packets from it whenever you need to collect signatures, onboard a new hire, or gather a set of related submissions. The Packets API lets you manage that whole lifecycle programmatically: build and maintain templates, kick off new packets, watch their progress submission by submission, and nudge anyone who has fallen behind.

Reach for this API when you want to automate sending bundles of forms (for example, generating an onboarding packet the moment a candidate accepts an offer), sync packet status into your own dashboards, or drive reminders from your own scheduling logic. If you only need a single form rather than a bundle, the Forms and Submissions APIs may be a better fit.
Basics
Every request goes to the base URL https://api.droplet.io, with the paths below appended to it. Authenticate by sending your JWT in the Authorization header on each call:
Authorization: Bearer <token>
Path parameters are shown with a leading colon, like /v1/packets/:id. Replace the whole placeholder (including the colon) with a real value.
All responses share a common envelope. A success looks like this:
{
"requestId": "req_8f2c1a",
"success": true,
"data": { /* ... */ }
}
On failure, success is false and an error object with code and message replaces data:
{
"requestId": "req_8f2c1a",
"success": false,
"error": { "code": "not_found", "message": "Packet not found" }
}
The data shape differs per endpoint and is documented with each one below. Mutating endpoints that act on a list (archive, restore, tags) return a simple { "count": N } telling you how many records were affected. Examples show the fields you will most commonly use; a record may include more, so ignore any you do not recognize.
Packet templates
Templates are the blueprints for your packets. They define which forms and documents are included, who gets assigned to each, the notifications that fire as the packet moves along, and the branding shown in the recipient portal.
List packet templates
GET /v1/packets/templates
Returns the packet templates that match your filters. Use it to populate a template picker or to find a template by name before starting a packet.
| Query param | Type | Description |
|---|---|---|
show | string | One of all, archived, active, voided. Defaults to active templates. |
search | string | Free-text search across template names and descriptions. |
include | string or array | Comma-separated extra fields to expand, e.g. name,description. |
tagKey | string | Filter to templates carrying this managed tag key. |
tagValue | string | Filter to templates whose tagKey has this value. |
curl https://api.droplet.io/v1/packets/templates?show=active&search=onboarding \
-H "Authorization: Bearer <token>"
data is an array of template objects.
{
"requestId": "req_a1",
"success": true,
"data": [
{
"id": "tmpl_9k2",
"name": "New Hire Onboarding",
"description": "Forms every new employee completes on day one",
"subject": { "role": "Employee" },
"forms": [
{
"type": "form",
"formId": "form_w4",
"name": "W-4 Withholding",
"assignee": { "type": "role", "name": "Employee" },
"loginRequired": false,
"published": true
}
// ...
],
"contributors": [{ "role": "HR Manager" }],
"notifications": [
{
"type": "packetReminder",
"label": "Weekly nudge",
"notificationTrigger": { "when": { "interval": 3, "unit": "day" } }
}
],
"created": "2026-01-04T12:00:00Z",
"modified": "2026-05-18T09:30:00Z",
"archived": false,
"status": "active",
"portalTitle": "Welcome to Droplet",
"tags": { "department": "people-ops" }
}
// ...
]
}
Create a packet template
POST /v1/packets/templates
Creates a new template. Only name is required; everything else can be filled in now or via a later update. The forms array lists the forms and documents the packet bundles, each optionally pre-assigned to a role or person.
| Body field | Type | Description |
|---|---|---|
name | string | Required. The template name. |
description | string | Optional summary shown to staff. |
subject | object | Who the packet is about, e.g. { "role": "Employee" }, or {} for none. |
forms | array | Items with type (form or document), formId, and an optional assignee / assignees (each role or person). |
contributors | array | Roles that can contribute to the packet, e.g. [{ "role": "Manager" }]. |
notifications | array | Reminder and event notifications (see note below). |
portalTitle, portalOrgName, portalDescription, portalLogoPath | string | Branding for the recipient portal. |
Each notification is one of two shapes. A packetReminder carries a notificationTrigger with a when (and optional repeating) interval and unit (minute, hour, day, week, month). Event notifications (packetAllCompleted, packetAllStarted, packetAnyRejected, packetAssignment) instead carry a recipients array of person or role entries.
curl -X POST https://api.droplet.io/v1/packets/templates \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{
"name": "New Hire Onboarding",
"description": "Day-one paperwork",
"subject": { "role": "Employee" },
"forms": [
{ "type": "form", "formId": "form_w4", "assignee": { "type": "role", "name": "Employee" } }
],
"notifications": [
{ "type": "packetAllCompleted", "recipients": [{ "type": "role", "name": "HR Manager" }] }
]
}'
data is the created template object, identical in shape to a list item above.
{
"requestId": "req_a2",
"success": true,
"data": { "id": "tmpl_9k2", "name": "New Hire Onboarding", "archived": false /* ... */ }
}
List forms available for templates
GET /v1/packets/templates/forms
Returns the forms and documents you can drop into a template's forms array. Use it to build a form picker when authoring a template.
curl https://api.droplet.io/v1/packets/templates/forms \
-H "Authorization: Bearer <token>"
data is an array of form references.
{
"requestId": "req_a3",
"success": true,
"data": [
{
"type": "form",
"formId": "form_w4",
"name": "W-4 Withholding",
"assignees": [{ "type": "role", "name": "Employee" }]
}
// ...
]
}
Retrieve a packet template
GET /v1/packets/templates/:packetTemplateId
Returns a single template by ID.
| Param | In | Description |
|---|---|---|
packetTemplateId | path | Required. The template ID. |
include | query | Comma-separated extra fields to expand, e.g. name,description. |
curl https://api.droplet.io/v1/packets/templates/tmpl_9k2 \
-H "Authorization: Bearer <token>"
data is a single template object, same shape as the create response.
Update a packet template
PUT /v1/packets/templates/:packetTemplateId
Updates an existing template. Send only the fields you want to change; the accepted body matches Create a packet template (all fields optional here, since the template already exists).
curl -X PUT https://api.droplet.io/v1/packets/templates/tmpl_9k2 \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{ "description": "Updated day-one paperwork", "portalTitle": "Welcome aboard" }'
data is the updated template object.
{
"requestId": "req_a4",
"success": true,
"data": { "id": "tmpl_9k2", "description": "Updated day-one paperwork" /* ... */ }
}
Update tags on packet templates
PUT /v1/packets/templates/tags
Adds or removes managed tags across one or more templates in a single call. Use set to assign key/value pairs and remove to drop keys.
| Body field | Type | Description |
|---|---|---|
ids | array | Required. The template IDs to update. |
set | object | Tag key/value pairs to set on each template. |
remove | array | Tag keys to remove from each template. |
curl -X PUT https://api.droplet.io/v1/packets/templates/tags \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{ "ids": ["tmpl_9k2", "tmpl_4p1"], "set": { "department": "people-ops" }, "remove": ["legacy"] }'
{ "requestId": "req_a5", "success": true, "data": { "count": 2 } }
Archive packet templates
POST /v1/packets/templates/archive
Archives one or more templates so they no longer appear in active lists. Archiving does not affect packets already started from a template.
| Body field | Type | Description |
|---|---|---|
packetTemplateIds | array | Required. The template IDs to archive. |
curl -X POST https://api.droplet.io/v1/packets/templates/archive \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{ "packetTemplateIds": ["tmpl_9k2"] }'
{ "requestId": "req_a6", "success": true, "data": { "count": 1 } }
Restore packet templates
POST /v1/packets/templates/restore
Brings previously archived templates back into active use.
| Body field | Type | Description |
|---|---|---|
packetTemplateIds | array | Required. The archived template IDs to restore. |
curl -X POST https://api.droplet.io/v1/packets/templates/restore \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{ "packetTemplateIds": ["tmpl_9k2"] }'
{ "requestId": "req_a7", "success": true, "data": { "count": 1 } }
Upload a template asset
POST /v1/packets/templates/asset
Uploads a file (such as a portal logo) for a template. Send the file contents as a string along with its MIME type and extension; the response gives you back the stored path, which you can then save to a write field like portalLogoPath. On reads, the rendered logo comes back as portalLogoUrl.
| Body field | Type | Description |
|---|---|---|
packetTemplateId | string | Required. The template the asset belongs to. |
file | string | Required. The file contents (for example, base64-encoded). |
fileType | object | Required. { "mime": "image/png", "ext": "png" }. |
curl -X POST https://api.droplet.io/v1/packets/templates/asset \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{
"packetTemplateId": "tmpl_9k2",
"file": "iVBORw0KGgoAAAANSUhEUgAA...",
"fileType": { "mime": "image/png", "ext": "png" }
}'
{ "requestId": "req_a8", "success": true, "data": { "path": "assets/tmpl_9k2/logo.png" } }
Packets
Packets are live instances started from a template. Each one tracks a subject, the people assigned to its forms, and a set of submissions that progress from draft to completed.
List packet IDs
GET /v1/packets/ids
Returns the IDs of packets matching your filters, plus a total count. This is the paginated entry point for browsing packets: fetch the matching IDs here, then hydrate the ones you need with Retrieve packets by ID.
| Query param | Type | Description |
|---|---|---|
page | integer | Zero-based page number. |
packetSubject | string | Filter by the packet's subject. |
packetTemplateId | string | Only packets started from this template. |
formId | string | Only packets that include this form. |
employeeIds | array | Filter to packets for these employees. |
status | string | Filter by packet status. |
show | string | One of all, archived, active, voided. |
search | string | Free-text search. |
created, modified | string | Date filters. |
tagKey, tagValue | string | Filter by managed tag. |
curl "https://api.droplet.io/v1/packets/ids?packetTemplateId=tmpl_9k2&status=pending&page=0" \
-H "Authorization: Bearer <token>"
{
"requestId": "req_b1",
"success": true,
"data": { "count": 42, "ids": ["pkt_001", "pkt_002", "pkt_003"] }
}
Retrieve packets by ID
POST /v1/packets/entities
Returns full packet records for a list of IDs. Pass the IDs in the body; this is the companion to List packet IDs for batch hydration.
| Field | In | Description |
|---|---|---|
ids | body | Required. Array of packet IDs to fetch. |
include | query | Comma-separated extra fields to expand, e.g. subject,initiatedBy. |
curl -X POST "https://api.droplet.io/v1/packets/entities?include=subject,initiatedBy" \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{ "ids": ["pkt_001", "pkt_002"] }'
data is an array of packet objects (the same shape returned by Retrieve a packet).
{
"requestId": "req_b2",
"success": true,
"data": [
{
"id": "pkt_001",
"name": "Onboarding - Jordan Lee",
"status": "pending",
"subject": { "type": "contributor", "name": "Jordan Lee", "email": "jordan@example.com" },
"packetTemplateId": "tmpl_9k2",
"initiatedBy": { "name": "Sam Rivera", "email": "sam@droplet.io" },
"packetSubmissions": [ /* ... */ ]
}
// ...
]
}
Retrieve a packet
GET /v1/packets/:id
Returns a single packet, including its submissions, assignees, status, and portal details.
| Param | In | Description |
|---|---|---|
id | path | Required. The packet ID. |
include | query | Comma-separated extra fields to expand, e.g. subject,initiatedBy. |
show | query | One of all, archived, active, voided. |
curl https://api.droplet.io/v1/packets/pkt_001 \
-H "Authorization: Bearer <token>"
{
"requestId": "req_b3",
"success": true,
"data": {
"id": "pkt_001",
"created": "2026-06-01T08:00:00Z",
"name": "Onboarding - Jordan Lee",
"organizationName": "Acme Co",
"packetPortalUrl": "https://portal.droplet.io/p/pkt_001",
"subject": { "type": "contributor", "name": "Jordan Lee", "email": "jordan@example.com" },
"packetTemplateId": "tmpl_9k2",
"status": "pending",
"initiatedBy": { "name": "Sam Rivera", "email": "sam@droplet.io" },
"packetSubmissions": [
{
"id": 5501,
"type": "submissions",
"formId": "form_w4",
"formName": "W-4 Withholding",
"assignedTo": [
{ "name": "Jordan Lee", "email": "jordan@example.com",
"emailStatus": { "status": "delivered" } }
],
"status": "pending",
"progress": { "stepLabel": "In progress", "percent": 40 },
"submissionUrl": "https://app.droplet.io/s/5501"
}
// ...
]
}
}
A packet's status is one of draft, pending, completed, or rejected. The subject can be a contributor (with name and email), a label, or an empty object when none is set. Each entry in packetSubmissions has a type of submissions (a form) or documentSubmissions (a document), so branch on both values.
Retrieve packet portal data
GET /v1/packets/:id/portal
Returns the same packet record shaped for portal access, the read-only view a recipient sees. Use it to render or mirror the recipient-facing experience.
| Param | In | Description |
|---|---|---|
id | path | Required. The packet ID. |
curl https://api.droplet.io/v1/packets/pkt_001/portal \
-H "Authorization: Bearer <token>"
data is a packet object in the same shape as Retrieve a packet, including packetSubmissions and portal branding fields.
List submissions for packets
POST /v1/packets/submissions
Returns every submission for one or more packets, complete with per-submission progress and permissions. Pass packet IDs in the body; the response is keyed by packet ID so you can fan out a single call across many packets.
| Body field | Type | Description |
|---|---|---|
ids | array | Required. Packet IDs whose submissions you want. |
curl -X POST https://api.droplet.io/v1/packets/submissions \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{ "ids": ["pkt_001", "pkt_002"] }'
data is an object mapping each packet ID to its array of submissions.
{
"requestId": "req_b4",
"success": true,
"data": {
"pkt_001": [
{
"id": 5501,
"type": "submissions",
"formId": "form_w4",
"formName": "W-4 Withholding",
"status": "completed",
"completed": "2026-06-03T14:22:00Z",
"progress": { "stepLabel": "Done", "percent": 100 }
}
// ...
]
// ...
}
}
Create a packet
POST /v1/packets
Starts a new packet from a template and sends it on its way. You name the subject, point at the template, and supply at least one submission describing each form or document and who it goes to.
| Body field | Type | Description |
|---|---|---|
subject | object | Required. Who the packet is about, in one of these shapes. A person or contributor: { "type": "person", "name": "...", "email": "..." }. A role: { "role": "..." }. A label: { "type": "label", "label": "..." }. |
packetTemplateId | string | Required. The template to start from. |
submissions | array | Required, at least one item. See the variants below. |
initiatedBy | object | Optional. The name and email of the person starting the packet. |
contributors | object | Optional contributors keyed by role. |
Each submissions item is one of three shapes:
- A form submission:
{ "type": "form", "formId": "...", "assignedTo": { "name": "...", "email": "..." }, "data": {} }. Thedataobject can prefill answers. - A document submission:
{ "type": "document", "formId": "...", "assignees": [{ "type": "person", "name": "...", "email": "..." }] }. - A reference to an existing submission by numeric
id:{ "id": 5501 }.
curl -X POST https://api.droplet.io/v1/packets \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{
"subject": { "type": "person", "name": "Jordan Lee", "email": "jordan@example.com" },
"packetTemplateId": "tmpl_9k2",
"submissions": [
{ "type": "form", "formId": "form_w4",
"assignedTo": { "name": "Jordan Lee", "email": "jordan@example.com" } }
],
"initiatedBy": { "name": "Sam Rivera", "email": "sam@droplet.io" }
}'
data is the newly created packet object, same shape as Retrieve a packet.
{
"requestId": "req_b5",
"success": true,
"data": { "id": "pkt_001", "status": "pending", "packetPortalUrl": "https://portal.droplet.io/p/pkt_001" /* ... */ }
}
Update tags on packets
PUT /v1/packets/tags
Adds or removes managed tags across one or more packets, mirroring the template tag endpoint.
| Body field | Type | Description |
|---|---|---|
ids | array | Required. The packet IDs to update. |
set | object | Tag key/value pairs to set. |
remove | array | Tag keys to remove. |
curl -X PUT https://api.droplet.io/v1/packets/tags \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{ "ids": ["pkt_001"], "set": { "cohort": "2026-Q2" } }'
{ "requestId": "req_b6", "success": true, "data": { "count": 1 } }
Archive packets
POST /v1/packets/archive
Archives one or more packets, removing them from active views.
| Body field | Type | Description |
|---|---|---|
ids | array | Required. The packet IDs to archive. |
curl -X POST https://api.droplet.io/v1/packets/archive \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{ "ids": ["pkt_001"] }'
{ "requestId": "req_b7", "success": true, "data": { "count": 1 } }
Restore packets
POST /v1/packets/restore
Restores previously archived packets back to active status.
| Body field | Type | Description |
|---|---|---|
ids | array | Required. The archived packet IDs to restore. |
curl -X POST https://api.droplet.io/v1/packets/restore \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{ "ids": ["pkt_001"] }'
{ "requestId": "req_b8", "success": true, "data": { "count": 1 } }
Send packet reminders
POST /v1/packets/:id/remind
Emails a reminder to every assignee in this packet who still has an incomplete submission. Optionally override the email's subject and body; omit emailTemplate to use the default reminder copy. The response tells you how many reminders went out.
| Field | In | Description |
|---|---|---|
id | path | Required. The packet ID. |
emailTemplate | body | Optional. Object with subject and body to customize the reminder. |
curl -X POST https://api.droplet.io/v1/packets/pkt_001/remind \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{ "emailTemplate": { "subject": "A quick nudge", "body": "Please wrap up your onboarding forms." } }'
{ "requestId": "req_b9", "success": true, "data": { "count": 2 } }