DonutworkHelp Center
Api ReferenceUtility Tools
Donutwork documentation

Share Secrets

Generate secure one-time links for sensitive data transmission with automated content purging and access tracking.

Share Secrets API

The Share Secrets API provides a secure mechanism for transmitting sensitive information (such as passwords, private keys, or PII) without leaving a persistent digital footprint. Secrets are encrypted and associated with a unique, short-lived URL. To ensure maximum security, content is purged immediately after the first successful access, rendering the link permanently invalid.


Secret Management

Audit Secret Inventory

Retrieve a paginated directory of all active (unread) or recently expired share secret entries. This allows for administrative tracking of shared links and their current consumption status.

GET
/2026-02-01/share-secrets.json
Required permissionshare_secrets:read

Path, query, and header parameters

sizeinteger· query
Maximum records per page (max 100).
pageinteger· query
Target page index.

Responses

Secret directory successfully retrieved.

{
  "entities": "ShareSecret",
  "count": 5,
  "per_page": 100,
  "pages": {
    "current": 1,
    "max": 1
  },
  "elements": [
    {
      "id": "6739782b...",
      "name": "Database Migration Credentials",
      "url": "https://hub.donutwork.com/ss/b6a9532aa4a06...",
      "readed": false
    }
  ]
}

Request examples

curl --request GET 'https://api.hub.donutwork.com/2026-02-01/share-secrets.json' \
  --header 'Authorization: Bearer YOUR_API_KEY'
const response = await fetch('https://api.hub.donutwork.com/2026-02-01/share-secrets.json', {
  method: 'GET',
  headers: {
    Authorization: 'Bearer ' + process.env.DONUTWORK_API_KEY,
  },
});
const requestId = response.headers.get('x-request-id');
if (!response.ok) {
  const error = await response.text();
  throw new Error(`${response.status} ${error} [${requestId}]`);
}
const contentType = response.headers.get('content-type') ?? '';
const data = response.status === 204 ? null
  : contentType.includes('application/json') ? await response.json()
  : await response.text();
$request = curl_init('https://api.hub.donutwork.com/2026-02-01/share-secrets.json');
curl_setopt_array($request, [
    CURLOPT_CUSTOMREQUEST => 'GET',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . getenv('DONUTWORK_API_KEY'),
    ],
]);
$response = curl_exec($request);
if ($response === false) {
    $error = curl_error($request);
    curl_close($request);
    throw new RuntimeException($error);
}
$status = curl_getinfo($request, CURLINFO_RESPONSE_CODE);
$contentType = (string) curl_getinfo($request, CURLINFO_CONTENT_TYPE);
curl_close($request);
if ($status >= 400) { throw new RuntimeException($response); }
$data = $status === 204 ? null
    : (str_contains($contentType, 'application/json')
        ? json_decode($response, true, flags: JSON_THROW_ON_ERROR) : $response);

Replace uppercase placeholders and sample values with your own data and company resource IDs. Keep the API key on a trusted server. Reuse an idempotency key only for the same logical request and payload.

Generate a new ephemeral secret link. The content provided is encrypted at rest and will be revealed only once. You can optionally trigger an automated email notification to the recipient.

Set requirePin to true to require a server-generated six-digit PIN before the recipient can reveal the content. The PIN is returned as pin_code only in the creation response; send it separately. Notification emails contain the link and optional message, never the PIN. Without requirePin, PIN protection defaults to false. Invalid recipient addresses or non-boolean requirePin values return 422 before creating a secret. If email delivery fails after creation, the response retains the link and reports sendTo.status: false; do not repeat creation just to retry delivery.

POST
/2026-02-01/share-secrets.json
Required permissionshare_secrets:write

Path, query, and header parameters

No path, query, or additional header parameters.

Request Body

JSON
{
  "shareSecret": {
    "name": "Deployment API Token",
    "content": "secret_value_redacted",
    "requirePin": true,
    "sendTo": {
      "email": "engineer@partner-company.com",
      "message": "The requested production API token is now available for one-time retrieval."
    }
  }
}
shareSecret.namestringRequired
Descriptive label for internal tracking.
shareSecret.contentstringRequired
The sensitive payload to be shared (OTV).
shareSecret.requirePinboolean
Require a generated six-digit PIN to read the secret. Default false. The PIN is returned only at creation and is never included in the email.
shareSecret.sendTo.emailstring
Recipient email for automated notification.
shareSecret.sendTo.messagestring
Optional custom message included in notification email.

Responses

Secret link successfully provisioned.

{
  "id": "67397a4c...",
  "name": "Deployment API Token",
  "url": "https://hub.donutwork.com/ss/fb89f610...",
  "readed": false,
  "pin_required": true,
  "pin_code": "123456",
  "readerInfo": {
    "ip": null,
    "ua": null,
    "isoDate": null,
    "ts": null
  },
  "sendTo": {
    "status": true,
    "messageId": "msg_9921"
  }
}

Request examples

curl --request POST 'https://api.hub.donutwork.com/2026-02-01/share-secrets.json' \
  --header 'Authorization: Bearer YOUR_API_KEY' \
  --header 'Content-Type: application/json' \
  --data '{
  "shareSecret": {
    "name": "Deployment API Token",
    "content": "secret_value_redacted",
    "requirePin": true,
    "sendTo": {
      "email": "engineer@partner-company.com",
      "message": "The requested production API token is now available for one-time retrieval."
    }
  }
}'
const response = await fetch('https://api.hub.donutwork.com/2026-02-01/share-secrets.json', {
  method: 'POST',
  headers: {
    Authorization: 'Bearer ' + process.env.DONUTWORK_API_KEY,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    "shareSecret": {
      "name": "Deployment API Token",
      "content": "secret_value_redacted",
      "requirePin": true,
      "sendTo": {
        "email": "engineer@partner-company.com",
        "message": "The requested production API token is now available for one-time retrieval."
      }
    }
  }),
});
const requestId = response.headers.get('x-request-id');
if (!response.ok) {
  const error = await response.text();
  throw new Error(`${response.status} ${error} [${requestId}]`);
}
const contentType = response.headers.get('content-type') ?? '';
const data = response.status === 204 ? null
  : contentType.includes('application/json') ? await response.json()
  : await response.text();
$request = curl_init('https://api.hub.donutwork.com/2026-02-01/share-secrets.json');
curl_setopt_array($request, [
    CURLOPT_CUSTOMREQUEST => 'POST',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . getenv('DONUTWORK_API_KEY'),
        'Content-Type: application/json',
    ],
    CURLOPT_POSTFIELDS => '{
  "shareSecret": {
    "name": "Deployment API Token",
    "content": "secret_value_redacted",
    "requirePin": true,
    "sendTo": {
      "email": "engineer@partner-company.com",
      "message": "The requested production API token is now available for one-time retrieval."
    }
  }
}',
]);
$response = curl_exec($request);
if ($response === false) {
    $error = curl_error($request);
    curl_close($request);
    throw new RuntimeException($error);
}
$status = curl_getinfo($request, CURLINFO_RESPONSE_CODE);
$contentType = (string) curl_getinfo($request, CURLINFO_CONTENT_TYPE);
curl_close($request);
if ($status >= 400) { throw new RuntimeException($response); }
$data = $status === 204 ? null
    : (str_contains($contentType, 'application/json')
        ? json_decode($response, true, flags: JSON_THROW_ON_ERROR) : $response);

Replace uppercase placeholders and sample values with your own data and company resource IDs. Keep the API key on a trusted server. Reuse an idempotency key only for the same logical request and payload.

Technical Implementation


Access & Revocation

Audit Access Metadata

Retrieve the access status and reader telemetry for a specific secret link. This endpoint provides audit trails (IP, User-Agent, Timestamp) once the secret has been consumed.

GET
/2026-02-01/share-secrets/{secretId}.json
Required permissionshare_secrets:read

Path, query, and header parameters

secretIdstring· pathRequired
The unique identifier of the secret entry.

Responses

Access metadata successfully retrieved.

{
  "id": "6739782b...",
  "name": "Database Migration Credentials",
  "readed": true,
  "readerInfo": {
    "ip": "82.1.4.22",
    "ua": "Mozilla/5.0...",
    "isoDate": "03/03/2026 11:20:00",
    "ts": 1772536800
  }
}

Request examples

curl --request GET 'https://api.hub.donutwork.com/2026-02-01/share-secrets/SECRETID.json' \
  --header 'Authorization: Bearer YOUR_API_KEY'
const response = await fetch('https://api.hub.donutwork.com/2026-02-01/share-secrets/SECRETID.json', {
  method: 'GET',
  headers: {
    Authorization: 'Bearer ' + process.env.DONUTWORK_API_KEY,
  },
});
const requestId = response.headers.get('x-request-id');
if (!response.ok) {
  const error = await response.text();
  throw new Error(`${response.status} ${error} [${requestId}]`);
}
const contentType = response.headers.get('content-type') ?? '';
const data = response.status === 204 ? null
  : contentType.includes('application/json') ? await response.json()
  : await response.text();
$request = curl_init('https://api.hub.donutwork.com/2026-02-01/share-secrets/SECRETID.json');
curl_setopt_array($request, [
    CURLOPT_CUSTOMREQUEST => 'GET',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . getenv('DONUTWORK_API_KEY'),
    ],
]);
$response = curl_exec($request);
if ($response === false) {
    $error = curl_error($request);
    curl_close($request);
    throw new RuntimeException($error);
}
$status = curl_getinfo($request, CURLINFO_RESPONSE_CODE);
$contentType = (string) curl_getinfo($request, CURLINFO_CONTENT_TYPE);
curl_close($request);
if ($status >= 400) { throw new RuntimeException($response); }
$data = $status === 204 ? null
    : (str_contains($contentType, 'application/json')
        ? json_decode($response, true, flags: JSON_THROW_ON_ERROR) : $response);

Replace uppercase placeholders and sample values with your own data and company resource IDs. Keep the API key on a trusted server. Reuse an idempotency key only for the same logical request and payload.

Manually revoke a secret link before it is consumed, or purge the audit record of an already read secret.

DELETE
/2026-02-01/share-secrets/{secretId}.json
Required permissionshare_secrets:write

Path, query, and header parameters

secretIdstring· pathRequired
The unique identifier to revoke.

Responses

Secret link successfully revoked and record purged.

{
  "id": "67397a4c...",
  "deleted": true
}

Request examples

curl --request DELETE 'https://api.hub.donutwork.com/2026-02-01/share-secrets/SECRETID.json' \
  --header 'Authorization: Bearer YOUR_API_KEY'
const response = await fetch('https://api.hub.donutwork.com/2026-02-01/share-secrets/SECRETID.json', {
  method: 'DELETE',
  headers: {
    Authorization: 'Bearer ' + process.env.DONUTWORK_API_KEY,
  },
});
const requestId = response.headers.get('x-request-id');
if (!response.ok) {
  const error = await response.text();
  throw new Error(`${response.status} ${error} [${requestId}]`);
}
const contentType = response.headers.get('content-type') ?? '';
const data = response.status === 204 ? null
  : contentType.includes('application/json') ? await response.json()
  : await response.text();
$request = curl_init('https://api.hub.donutwork.com/2026-02-01/share-secrets/SECRETID.json');
curl_setopt_array($request, [
    CURLOPT_CUSTOMREQUEST => 'DELETE',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . getenv('DONUTWORK_API_KEY'),
    ],
]);
$response = curl_exec($request);
if ($response === false) {
    $error = curl_error($request);
    curl_close($request);
    throw new RuntimeException($error);
}
$status = curl_getinfo($request, CURLINFO_RESPONSE_CODE);
$contentType = (string) curl_getinfo($request, CURLINFO_CONTENT_TYPE);
curl_close($request);
if ($status >= 400) { throw new RuntimeException($response); }
$data = $status === 204 ? null
    : (str_contains($contentType, 'application/json')
        ? json_decode($response, true, flags: JSON_THROW_ON_ERROR) : $response);

Replace uppercase placeholders and sample values with your own data and company resource IDs. Keep the API key on a trusted server. Reuse an idempotency key only for the same logical request and payload.

Security Policy: Content retrieval is handled via the specific url field returned during provisioning. Once the readed flag is set to true, the content is physically deleted from the database and cannot be recovered.

On this page