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.