SOLENOID.SYSTEMS [...]
SYS ENG PHI PRICING
API
├─ CATCH ├─ GATE ├─ KEY ├─ LATCH ├─ METER ├─ PULSE ├─ RELAY └─ WITNESS

Ship API Keys to Your Users in 5 Minutes

#api-keys #authentication #key #tutorial

You’re building an API product. Users need API keys. You need to generate them, verify them fast, let users rotate them, and revoke them when they’re compromised.

You could build a keys table in Postgres. Hash the secrets with bcrypt. Query on every request. Add caching. Handle cache invalidation. Build a rotation grace period. Track last_used_at.

Or you could use Key.

The 5-Minute Version

Key manages API keys as a service.

Step 1: Create a key for your user

curl -X POST https://api.solenoid.systems/v1/keys \
  -H "Authorization: Bearer sm_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "prefix": "myapp_live",
    "name": "Production Key",
    "scopes": ["orders:read", "orders:write"]
  }'

Response:

{
  "key_id": "550e8400-e29b-41d4-a716-446655440000",
  "secret": "myapp_live_A1B2C3D4E5F6G7H8I9J0_12ab34cd",
  "prefix": "myapp_live",
  "name": "Production Key",
  "scopes": ["orders:read", "orders:write"],
  "created_at": "2026-02-05T12:00:00Z"
}

The secret is only returned at creation. Give it to your user. They use it to authenticate.

Step 2: Verify keys on each request

// Your API endpoint
app.get('/api/orders', async (req, res) => {
  const apiKey = req.headers.authorization?.replace('Bearer ', '')

  const verification = await fetch('https://api.solenoid.systems/v1/keys/verify', {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${process.env.SOLENOID_API_KEY}`,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({ secret: apiKey })
  })

  const result = await verification.json()

  if (!result.valid) {
    return res.status(401).json({ error: 'Invalid API key' })
  }

  if (!result.scopes.includes('orders:read')) {
    return res.status(403).json({ error: 'Insufficient permissions' })
  }

  // Fetch and return orders
  const orders = await db.orders.findByUserId(result.user_id)
  res.json(orders)
})

Verification is sub-millisecond. Globally distributed. Cached at the edge.

Step 3: User rotates their key

curl -X POST https://api.solenoid.systems/v1/keys/550e8400-e29b-41d4-a716-446655440000/rotate \
  -H "Authorization: Bearer sm_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{"grace_hours": 24}'

Response:

{
  "new_key_id": "660f9511-f3ac-52e5-b827-557766551111",
  "new_secret": "myapp_live_Z9Y8X7W6V5U4T3S2R1Q0_56ef78gh",
  "old_key_id": "550e8400-e29b-41d4-a716-446655440000",
  "old_expires_at": "2026-02-06T12:00:00Z",
  "grace_hours": 24
}

Both keys work for 24 hours. User updates their code. Old key expires automatically.

Handling the Edge Cases

Custom prefixes: Users recognize your keys instantly.

# Stripe-style prefixes
{"prefix": "sk_live"} sk_live_ABC123...
{"prefix": "sk_test"} sk_test_XYZ789...

# Environment-aware prefixes
{"prefix": "acme_prod"} acme_prod_ABC123...
{"prefix": "acme_dev"} acme_dev_XYZ789...

Scopes: Define your own scope vocabulary. Any string works.

curl -X POST https://api.solenoid.systems/v1/keys \
  -H "Authorization: Bearer sm_your_api_key" \
  -d '{
    "prefix": "myapp_live",
    "name": "Read-Only Key",
    "scopes": ["orders:read", "billing:read"]
  }'

Verify scopes in your code: result.scopes.includes('orders:write').

Metadata tracking: Store custom data with each key.

curl -X POST https://api.solenoid.systems/v1/keys \
  -H "Authorization: Bearer sm_your_api_key" \
  -d '{
    "prefix": "myapp_live",
    "name": "Production Key",
    "scopes": ["orders:read"],
    "metadata": {
      "team": "engineering",
      "created_by": "alice@example.com",
      "purpose": "CI/CD pipeline"
    }
  }'

Revocation: Key compromised? Revoke immediately.

curl -X DELETE https://api.solenoid.systems/v1/keys/550e8400-e29b-41d4-a716-446655440000 \
  -H "Authorization: Bearer sm_your_api_key"

Verification fails instantly across all edge locations.

Building a Key Management UI

List user keys:

app.get('/dashboard/api-keys', async (req, res) => {
  const userId = req.session.userId

  const response = await fetch('https://api.solenoid.systems/v1/keys', {
    headers: { 'Authorization': `Bearer ${process.env.SOLENOID_API_KEY}` }
  })

  const { keys } = await response.json()

  res.render('api-keys', {
    keys: keys.map(k => ({
      id: k.id,
      name: k.name,
      masked: k.masked_secret,
      scopes: k.scopes,
      created: new Date(k.created_at).toLocaleDateString(),
      lastUsed: k.last_used_at
        ? new Date(k.last_used_at).toLocaleDateString()
        : 'Never'
    }))
  })
})

Create key from UI:

app.post('/dashboard/api-keys/create', async (req, res) => {
  const { name, scopes } = req.body

  const response = await fetch('https://api.solenoid.systems/v1/keys', {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${process.env.SOLENOID_API_KEY}`,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      prefix: 'myapp_live',
      name,
      scopes
    })
  })

  const key = await response.json()

  // Show secret once, never stored
  res.render('key-created', {
    secret: key.secret,
    warning: 'Copy this now. It will not be shown again.'
  })
})

Rotate key:

app.post('/dashboard/api-keys/:id/rotate', async (req, res) => {
  const { id } = req.params

  const response = await fetch(
    `https://api.solenoid.systems/v1/keys/${id}/rotate`,
    {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${process.env.SOLENOID_API_KEY}`,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({ grace_hours: 24 })
    }
  )

  const result = await response.json()

  res.json({
    new_secret: result.new_secret,
    old_expires: result.old_expires_at
  })
})

Usage Tracking

Each key records last_used_at, last_used_ip, and last_used_ua automatically from request headers during verification:

curl -X POST https://api.solenoid.systems/v1/keys/verify \
  -H "Authorization: Bearer sm_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{"secret": "myapp_live_ABC..."}'

Check usage via the Get Key endpoint: GET /v1/keys/:id.

Why Not Build This Yourself?

API key management seems simple. Generate UUIDs. Hash with bcrypt. Store in a table.

Then you need verification to be fast. Add caching. Handle cache invalidation races. Revocations sometimes take 30 seconds to propagate.

Then you need rotation with grace periods. Track old and new keys. Ensure both verify. Clean up expired keys.

Then you need global low latency. Deploy to multiple regions. Replicate the keys table. Handle replication lag.

Key runs on Cloudflare Workers with D1 and KV. Sub-millisecond verification globally. Atomic revocation. Usage tracking built in.

You define scopes and prefixes. Key handles the rest.

Try It Now

Free tier: 10,000 calls to prototype. Pro tier ($49.99/mo) and above: unlimited Key usage.

# Create a key
curl -X POST https://api.solenoid.systems/v1/keys \
  -H "Authorization: Bearer sm_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "prefix": "test",
    "name": "Test Key",
    "scopes": ["read", "write"]
  }'

# Verify it (use the secret from create response)
curl -X POST https://api.solenoid.systems/v1/keys/verify \
  -H "Authorization: Bearer sm_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{"secret": "test_YOUR_SECRET_HERE"}'

# Revoke it
curl -X DELETE https://api.solenoid.systems/v1/keys/{key_id} \
  -H "Authorization: Bearer sm_your_api_key"

Your API key management, in three API calls.