Drop lifecycle
Use this API to create an encrypted drop, retrieve it by its secret key, or provide a one-time retrieval path to a recipient. The owner-facing routes require a bearer token and enforce ownership. See Authorization for token, principal, and drop-key requirements.
Routes
| Route | Required fields | Optional fields and behavior |
|---|---|---|
POST /v1/drop/reserve |
None | Creates a reserved drop. Accepts the reservation fields below. |
POST /v1/drop/activate |
id, payload or data |
Adds the payload to a reserved drop and makes it active. |
POST /v1/drop/store |
payload or data |
Creates an active drop in one call. |
POST /v1/drop/get |
id |
With no non-empty key, returns owner-visible summary metadata. With key, returns the decrypted payload. |
POST /v1/drop/status |
id |
Returns owner-visible summary metadata. |
POST /v1/drop/list |
None | Lists the caller's drops. Supports kind, status, and pagination. |
POST /v1/drop/revoke |
id |
Revokes a reserved or active unexpired drop and clears its payload. |
POST /v1/drop/delete |
id |
Deletes an owned drop. An admin or root principal may delete any drop. |
POST /v1/drop/consume |
id, key |
Reads and consumes an owned active drop. |
POST /v1/consume |
id, key |
Reads and consumes an active drop. It needs no bearer token only when anonymous is true; private drops require the owner's bearer token. |
Reserve a drop
Reserve a drop when the identifier or delivery metadata must exist before the
payload. This route creates a reserved drop and does not accept a payload.
| Field | Required | Type | Description |
|---|---|---|---|
id |
No | String | A 64-character hexadecimal ID. The API generates one when omitted. |
anonymous |
No | Boolean | Defaults to false. Set true to allow /v1/consume without an owner token. |
kind |
No | String | Caller-defined category; defaults to null. |
metadata |
No | JSON value | Caller-supplied metadata; defaults to null. |
expires_at |
No | Number or String | Future Unix timestamp or date-time. Defaults to 24 hours from creation. expires is an alias. |
ttl |
No | Integer | Lifetime in seconds, at least 1. Used only when neither expiration field is supplied. |
payload, data, key, payload_format, and content_type do not affect a
reservation.
curl -sS https://api.drop.m7.org/v1/drop/reserve \
-H 'Authorization: Bearer ACCESS_TOKEN' \
-H 'Content-Type: application/json' \
-d '{
"anonymous": true,
"kind": "handoff",
"metadata": { "case": "EXAMPLE-001" },
"ttl": 3600
}'
The response's data.id is used to activate, inspect, revoke, or delete the
drop.
Activate a reserved drop
Only the owner can activate a reserved drop. The drop must not be expired.
Activation writes an encrypted payload and changes the status to active.
| Field | Required | Type | Description |
|---|---|---|---|
id |
Yes | String | Reserved drop ID. |
payload |
Conditional | JSON value or String | Payload to encrypt. Required unless data is supplied. Takes precedence when both are sent. |
data |
Conditional | JSON value or String | Accepted alias for payload; use payload in new integrations. |
key |
No | String | 64-character hexadecimal payload key. The API generates and returns one when omitted. |
payload_format |
No | String | json, text, html, or base64. When omitted, an array payload becomes json; another payload becomes text. |
content_type |
No | String | Media type override. Defaults to application/json, text/plain; charset=UTF-8, text/html; charset=UTF-8, or application/octet-stream according to the payload format. |
anonymous |
No | Boolean | When omitted, retains the reservation's value. |
kind |
No | String | When omitted, retains the reservation's value. |
metadata |
No | JSON value | When omitted, retains the reservation's value. |
expires_at |
No | Number or String | Replaces the expiration time; defaults to the reservation's expiration time. expires is an alias. |
ttl |
No | Integer | Lifetime in seconds, at least 1. Used only when neither expiration field is supplied. |
curl -sS https://api.drop.m7.org/v1/drop/activate \
-H 'Authorization: Bearer ACCESS_TOKEN' \
-H 'Content-Type: application/json' \
-d '{
"id": "DROP_ID",
"payload": { "message": "Example payload" },
"payload_format": "json"
}'
{
"status": 1,
"comment": "OK",
"data": {
"id": "DROP_ID",
"status": "active",
"payload_format": "json",
"key": "DROP_KEY"
}
}
Save data.key securely before giving the ID and key to a recipient. The API
does not return a generated key on later summary reads.
Store an active drop
Use POST /v1/drop/store to create and activate a drop in one call. Its
payload fields have the same meaning as activation. It also accepts the
following creation fields:
| Field | Required | Type | Description |
|---|---|---|---|
id |
No | String | A 64-character hexadecimal ID. The API generates one when omitted. |
anonymous |
No | Boolean | Defaults to false. |
kind |
No | String | Caller-defined category; defaults to null. |
metadata |
No | JSON value | Caller-supplied metadata; defaults to null. |
expires_at |
No | Number or String | Future expiration; defaults to 24 hours from creation. expires is an alias. |
ttl |
No | Integer | Lifetime in seconds, at least 1. Used only when neither expiration field is supplied. |
curl -sS https://api.drop.m7.org/v1/drop/store \
-H 'Authorization: Bearer ACCESS_TOKEN' \
-H 'Content-Type: application/json' \
-d '{
"payload": "Example payload",
"payload_format": "text",
"anonymous": true,
"ttl": 3600
}'
The response has a drop summary plus a generated data.id and data.key
when those fields were omitted from the request.
Inspect a drop or read its payload
POST /v1/drop/get always requires the caller to own the drop.
| Request | Result |
|---|---|
{ "id": "DROP_ID" } |
Summary metadata only. This works even after the drop expires. |
{ "id": "DROP_ID", "key": "DROP_KEY" } |
Summary metadata and decrypted payload. The drop must be active and unexpired. |
The keyed read records accessed_at but does not change the lifecycle state;
its consumed and cleared response fields are both false.
POST /v1/drop/status accepts only id and returns the same owner-visible
summary. It does not need the payload key and does not return a payload.
Retrieve and consume a drop
Consumption returns the decrypted payload and then changes the drop to
consumed. It clears the stored payload, so the operation cannot be retried.
| Route | Authorization and ownership |
|---|---|
POST /v1/drop/consume |
Bearer token required. The caller must own the drop. |
POST /v1/consume |
No token is required when the drop has anonymous: true. A private drop requires a bearer token for its owner. |
Both routes require id and key; both require an active, unexpired drop.
Their success data contains the drop summary, decrypted payload,
consumed: true, and cleared: true.
curl -sS https://api.drop.m7.org/v1/consume \
-H 'Content-Type: application/json' \
-d '{
"id": "DROP_ID",
"key": "DROP_KEY"
}'
{
"status": 1,
"comment": "OK",
"data": {
"id": "DROP_ID",
"status": "consumed",
"payload": "Example payload",
"consumed": true,
"cleared": true
}
}
List, revoke, and delete drops
List owned drops
POST /v1/drop/list returns only drops owned by the bearer-token principal.
It accepts the shared pagination fields plus:
| Field | Required | Type | Description |
|---|---|---|---|
kind |
No | String | Exact category filter. |
status |
No | String | reserved, active, consumed, revoked, or expired. The expired filter selects records whose expires_at has passed. |
owner |
No | String | Ignored. The route always scopes results to the caller. |
Revoke a drop
POST /v1/drop/revoke requires id. Only the owner may revoke, and the drop
must be reserved or active and not expired. The result is a summary with
status: "revoked"; the stored payload is cleared.
Delete a drop
POST /v1/drop/delete requires id and removes an owned drop. An admin or
root principal may delete any drop through this route. Its success data is:
{
"id": "DROP_ID",
"deleted": true
}
Errors and state
| Condition | Result |
|---|---|
Invalid id or key |
The value must be a 64-character hexadecimal string. |
Missing payload and data |
Store and activate fail. |
Unsupported payload_format |
Use only json, text, html, or base64. |
Invalid ttl or expiration |
ttl must be at least 1; expiration must be in the future. |
| Duplicate create ID | Reserve and store fail with a conflict. |
| Keyed read or consume of an expired drop | Fails with 410. Summary inspection remains available to the owner. |
| Consume of a non-active drop | Fails with a lifecycle conflict. |
Activate outside reserved |
Fails with a lifecycle conflict. |
Revoke outside an unexpired reserved or active state |
Fails with a lifecycle conflict. |