Authentication
Every request (except /health and /machine/keys/bootstrap) requires an API key passed in the X-API-Key header.
Passing your API key
Section titled “Passing your API key”curl https://apis.jameslin.info/machine/snapshots \ -H "X-API-Key: mk_a1b2c3d4e5f6..."const res = await fetch('https://apis.jameslin.info/machine/snapshots', { headers: { 'X-API-Key': 'mk_a1b2c3d4e5f6...' },});Key format
Section titled “Key format”mk_<64 hex characters>- Prefix:
mk_(machine key) - Body: 64 hex characters (32 random bytes)
How keys are stored
Section titled “How keys are stored”Keys are never stored in plaintext. When a key is created:
- The raw key is returned to you once
- The key is hashed with SHA-256
- Only the hash is stored in the database
If you lose a key, it cannot be recovered — create a new one.
Key scoping
Section titled “Key scoping”Each API key is scoped to exactly one project and carries two pieces of identity:
| Property | Description |
|---|---|
apiKeyId | Unique identifier for the key |
projectId | The project this key belongs to |
Unauthenticated endpoints
Section titled “Unauthenticated endpoints”| Method | Path | Purpose |
|---|---|---|
GET | /health | Health check |
POST | /machine/keys/bootstrap | Create first project + key |
Error responses
Section titled “Error responses”| Status | Error | Meaning |
|---|---|---|
401 | Missing X-API-Key header | No key provided |
401 | Invalid API key | Key doesn’t match any stored hash |
403 | Forbidden: requires one of [...] | Valid key but insufficient role |
See Roles & Permissions for details on what each role can access.