Skip to main content

🔑 Create S3 Keys

S3 credentials (an access key + secret key pair) authenticate every request to the S3 API. You create them yourself using your existing Lighthouse API key — one call, no dashboard round-trip. An account can hold multiple keys at once (up to 10 active), each labelled and independently revocable — one for production, one for staging, one per app.

Why a separate keypair?

Your Lighthouse API key is a bearer token; S3 clients instead sign every request with a secret that never travels over the wire (AWS Signature V4). That's what makes presigned URLs and tamper-proof requests possible. Your API key stays server-side; the S3 keypair is what you put into aws configure, CI, and apps.


Create a key

Send your Lighthouse API key as a Bearer token. The note label is optional but recommended once you have more than one key:

curl -X POST https://s3.lighthouse.storage/-/keys \
-H "Authorization: Bearer YOUR_LIGHTHOUSE_API_KEY" \
-H "Content-Type: application/json" \
-d '{"note": "production"}'

Response:

{
"accessKey": "LHAK7F3A9C21D48E55B0",
"secretKey": "3q9Zt...",
"note": "production",
"region": "auto",
"message": "save the secret key now — it cannot be shown again"
}
Secret shown once

The secret key is returned only in this response and cannot be recovered afterwards — not by you, not by support. If it's lost, revoke that key and create a new one.

Up to 10 keys can be active at once; creating beyond the cap returns 409 until you revoke one. All of an account's keys see the same buckets — buckets belong to your account, not to an individual key.


Create keys with a session JWT (dApp frontends)

If your app authenticates users with a Lighthouse session JWT instead of an API key, pass the JWT as the Bearer token and the user's permanent API key in the body:

const res = await fetch('https://s3.lighthouse.storage/-/keys', {
method: 'POST',
headers: {
Authorization: `Bearer ${sessionJwt}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ apiKey: userApiKey, note: 'my-app' }),
})

Why both? The JWT proves who the user is, but whatever key is bound at creation becomes the account's upload/billing key — and JWTs expire, so binding one would silently break uploads later. The body apiKey must belong to the same account as the JWT (403 otherwise), so one user can never bind another account's key.

note

A POST with only a JWT returns 400 with this explanation. A plain API-key Bearer needs no apiKey in the body. Listing and revoking (below) work with a JWT alone.


List your keys

Works with an API key or a JWT. Returns your full key history, newest first — secrets are never included:

curl https://s3.lighthouse.storage/-/keys \
-H "Authorization: Bearer YOUR_API_KEY_OR_JWT"
{
"keys": [
{"accessKey": "LHAK7F3A9C21D48E55B0", "note": "production", "disabled": false, "created": "2026-07-16T09:12:03Z"},
{"accessKey": "LHAK52C1A0B93E7D66F2", "note": "staging", "disabled": false, "created": "2026-07-15T14:02:51Z"},
{"accessKey": "LHAKB339D445CE7BBB26", "note": "old-laptop", "disabled": true, "created": "2026-07-13T17:34:27Z"}
],
"region": "auto"
}

Revoke a key

Revoke a specific key by access key (recommended), or the newest active one (works with an API key or a JWT):

# a specific key
curl -X DELETE https://s3.lighthouse.storage/-/keys/LHAK52C1A0B93E7D66F2 \
-H "Authorization: Bearer YOUR_API_KEY_OR_JWT"

# the newest active key
curl -X DELETE https://s3.lighthouse.storage/-/keys \
-H "Authorization: Bearer YOUR_API_KEY_OR_JWT"

Revocation takes effect within about 30 seconds; requests signed with that key (including its presigned URLs) start failing with InvalidAccessKeyId. Your buckets and files are untouched — they belong to your account, and every other key (and any future key) still reaches them. Revoking also frees a slot under the 10-key cap.

To rotate a key: create the replacement first, roll it out to your apps, then revoke the old one — zero downtime.


Response codes

CodeMeaning
201Key created — the only time you'll see the secret
400JWT auth without an apiKey in the body
401Lighthouse rejected the token (or the body apiKey)
403The body apiKey belongs to a different account than the JWT
404(on DELETE /-/keys/{accessKey}) no such key on your account
409Active key limit (10) reached — revoke an unused key first
429Slow down — the endpoint is rate-limited