If you're an engineer, secret-sharing usually shows up in three places: onboarding, vendor hand-off, and incident response. Here's a developer's-eye view of when to reach for a one-time link, when to skip it, and how to script it without leaking the secret into your shell history along the way.
When to use a one-time link
- Onboarding a new engineer with their first set of credentials.
- Sharing a temporary admin password during an incident.
- Delivering a vendor or contractor token that should not live in your vault.
- Rotating a credential where the old value should die promptly.
- Sending a customer the [API key](/glossary#api-key "Glossary: api key") you provisioned for them.
When not to use a one-time link
- Machine-to-machine secrets. CI environment variables, K8s secrets, and Vault dynamic credentials are the right primitive there. A link is for the human hand-off into those systems.
- Long-lived team credentials. Those belong in a password manager.
- Public configuration. Don't dress up non-secret config as a secret. Use a regular link.
The dividing line: if both ends of the transfer are machines, use your platform's secret store. If at least one end is a human, a one-time link is usually the cleanest primitive.
| Situation | Right primitive |
|---|---|
| New engineer's first credentials | One-time secret link |
| CI pipeline needs a deploy token at runtime | CI secret store / env vars |
| Pod needs a database password | K8s secret / external secrets operator |
| Team login used daily by five people | Password manager (shared vault) |
| Customer needs their provisioned API key | One-time secret link |
| Secret needed inside a git repo | Never — reference a secret store; add scanning to CI |
Scripting it
LinkPilot has a REST API (available on paid plans). Issuing a secret from a script looks like this:
LP_KEY=lp_live_...
URL=$(curl -sS https://api.uselinkpilot.com/v1/secrets \
-H "Authorization: Bearer $LP_KEY" \
-H "Content-Type: application/json" \
-d '{
"payload": "'"$NEW_DB_PASSWORD"'",
"expires_in": 3600,
"burn_after_read": true,
"passphrase": "'"$ONBOARDING_PASSPHRASE"'"
}' | jq -r '.url')
echo "Secret link: $URL"
Pipe that URL into whatever delivery channel you already use — Slack DM, email, your onboarding tool's webhook.
Keeping the payload out of the process list
The snippet above has a classic flaw: interpolating the payload into
-d puts it on curl's command line, where any local user can read
it via ps while the request runs. Build the JSON with jq and
feed it over stdin instead:
set -euo pipefail
jq -n --arg payload "$NEW_DB_PASSWORD" \
--arg pass "$ONBOARDING_PASSPHRASE" \
'{payload: $payload, expires_in: 3600,
burn_after_read: true, passphrase: $pass}' |
curl -sS https://api.uselinkpilot.com/v1/secrets \
-H "Authorization: Bearer $LP_KEY" \
-H "Content-Type: application/json" \
-d @- | jq -r '.url'
Three habits worth making reflexive:
- Values arrive via environment variables or a secrets manager CLI — never typed into an interactive shell where they land in history.
- The script prints the link, never the payload.
set -euo pipefailso a failed API call can't silently hand an empty URL to the next step.
Onboarding template
A pattern that works well for new engineers on day one:
- Onboarding script provisions accounts and pulls one-time credentials.
- Script creates a secret link per credential with a 4-hour expiry and a shared passphrase.
- Script sends an email with all the links and a Slack DM with the passphrase.
- Engineer opens each link, copies the credential into their password manager.
- Anything unread by end-of-day gets revoked and reissued at their pace.
Why these parameters: the 4-hour expiry covers a normal first morning without leaving credentials live overnight; the passphrase travels by a second channel so a forwarded or misdelivered email is harmless; burn-after-read means the engineer's mailbox ends the day containing only dead URLs.
Tell the new hire explicitly: "each link works once — save the value into your password manager before closing the tab." The most common onboarding failure is a recipient who reads the secret, closes the tab, and assumes they can come back later.
A rotation runbook
Rotating a shared database password with a contractor or teammate on the receiving end:
- Generate the new password and apply it in the source system (database, cloud console, wherever it's canonical).
- Update your own consumers first — CI vault, K8s secret, app config — so nothing breaks while the human hand-off happens.
- Create a one-time link for the new value: burn after read, 1-hour expiry, passphrase.
- Deliver link and passphrase through different channels.
- Verify the view event in the audit timeline. No view before expiry? Revoke and reissue — never re-send the same link.
- Confirm the old credential is disabled at the source.
Each rotation gets its own link. Links are independent by design — that's a feature for delivery and a non-feature for governance, so the rotation schedule lives in your manager or runbook, not in the link tool.
Incident response
During an incident you often need to rotate a credential and distribute the new one to a few responders fast. Don't paste it in the incident channel — that channel is being recorded for the postmortem, and the secret will end up in the export, the retro doc, and every retention system downstream of them.
- Rotate the credential in the source system.
- Issue a secret link with a 30-minute expiry.
- Drop the link in the channel; send the passphrase by DM.
- After the incident, confirm the link is burned or revoked.
The postmortem then contains a dead URL instead of a live admin password — and the audit trail gives the timeline reviewer an exact "credential re-issued at 14:32, read at 14:33" record for free.
One unfurl-related note: chat clients fetch pasted URLs to render
previews. A well-designed secret link survives this because the
preview fetch is a GET and the reveal is an explicit POST — see
link scanners and one-time secrets
for the full mechanics. If you're evaluating a tool, test this
before an incident, not during one.
What the audit trail gives you
Every LinkPilot secret carries an event timeline: created, viewed, failed_passphrase, burned, expired, revoked. Views are recorded with a timestamp, coarse geolocation when available, the user agent, and a hashed IP (SHA-256 with a daily-rotating salt — the raw IP is never stored). In practice that gives you three answers scripts and runbooks can rely on:
- Delivery confirmation — a viewed event means the hand-off happened.
- Anomaly detection — a failed_passphrase burst or a view you don't recognize is your cue to rotate immediately.
- A clean record for reviews — "delivered once, read once, burned" is a sentence auditors like.
Limits: what links don't solve
- They're not a vault. Nothing about a one-time link manages ongoing access, rotation schedules, or who-has-what inventories.
- They're not end-to-end encrypted. LinkPilot says this plainly: payloads are TLS-protected in transit, encrypted at rest, and irreversibly overwritten on burn/expiry/revoke — but it does not claim E2E encryption. Short expiry plus burn-after-read is the honest guarantee; details on the security architecture page.
- They don't fix secret sprawl in code. Keys hardcoded in repos need scanning and rotation, not a better delivery mechanism.
- They can't protect the far end. Once a human has read the value, treat it as living wherever they put it.
Try it free, no signup
Free Secret Link Generator
Send encrypted, self-destructing messages securely.