Extension API Reference
Technical documentation for integrating with the SN Utils extension API.
This technical reference is for developers building integrations with SN Utils or understanding how the extension communicates with our backend.
Overview
The SN Utils extension uses a three-token authentication system:
| Token | Lifetime | Purpose |
|---|---|---|
| Activation Token | 5 minutes | One-time device activation |
| Access Token | 7 days | API authentication |
| Refresh Token | 30 days | Renew access tokens |
Base URL
Production: https://snutils.com/api
Authentication
Most API endpoints require a valid access token in the Authorization header:
Authorization: Bearer <access_token>
License Endpoints
Activate Device
Exchange an activation token for access and refresh tokens.
POST /api/license/activate
Content-Type: application/json
{
"token": "eyJhbGciOiJIUzI1NiIs...",
"deviceFingerprint": "unique-device-id",
"deviceName": "Chrome on MacBook Pro"
}
Success Response:
{
"success": true,
"deviceId": "uuid",
"accessToken": "eyJhbGciOiJIUzI1NiIs...",
"refreshToken": "eyJhbGciOiJIUzI1NiIs...",
"expiresAt": "2025-01-22T12:00:00Z",
"accountSlug": "team-slug",
"email": "user@example.com"
}
Validate Token
Check if an activation token is valid before activating.
POST /api/license/validate
Content-Type: application/json
{
"token": "eyJhbGciOiJIUzI1NiIs...",
"deviceFingerprint": "unique-device-id"
}
Extension Endpoints
Refresh Tokens
Get new tokens when the access token expires.
POST /api/extension/refresh
Content-Type: application/json
{
"refreshToken": "eyJhbGciOiJIUzI1NiIs...",
"deviceFingerprint": "unique-device-id"
}
Response:
{
"success": true,
"accessToken": "eyJhbGciOiJIUzI1NiIs...",
"refreshToken": "eyJhbGciOiJIUzI1NiIs...",
"expiresAt": "2025-01-29T12:00:00Z"
}
Heartbeat
Periodic license validation (recommended: every 6 hours).
POST /api/extension/heartbeat
Authorization: Bearer <access_token>
Content-Type: application/json
{
"deviceFingerprint": "unique-device-id"
}
Response:
{
"valid": true,
"accountSlug": "team-slug",
"email": "user@example.com"
}
Backup Endpoints
List Backups
GET /api/extension/backup Authorization: Bearer <access_token>
Query Parameters:
type— Filter by backup type:full,settings,scripts,snippetsid— Get a specific backup by UUID
Response:
{
"success": true,
"backups": [
{
"id": "uuid",
"backup_type": "full",
"backup_name": "My Backup",
"data_version": 1,
"data_size_bytes": 2048,
"created_at": "2025-01-15T10:00:00Z",
"updated_at": "2025-01-15T10:00:00Z"
}
],
"stats": {
"total_count": 5,
"total_size_bytes": 10240
},
"limits": {
"maxBackupSize": 5242880,
"maxTotalSize": 52428800,
"maxBackupCount": 20
}
}
Get Specific Backup
GET /api/extension/backup?id=<uuid> Authorization: Bearer <access_token>
Response:
{
"success": true,
"backup": {
"id": "uuid",
"backup_type": "full",
"backup_name": "My Backup",
"data": {
"settings": { "theme": "dark" },
"scripts": [],
"snippets": []
},
"data_version": 1,
"data_size_bytes": 2048,
"created_at": "2025-01-15T10:00:00Z"
}
}
Create Backup
POST /api/extension/backup
Authorization: Bearer <access_token>
Content-Type: application/json
{
"backupType": "full",
"backupName": "My Settings Backup",
"data": {
"settings": { "theme": "dark", "fontSize": 14 },
"scripts": [],
"snippets": []
},
"dataVersion": 1
}
Response:
{
"success": true,
"backup": {
"id": "new-uuid",
"backup_type": "full",
"backup_name": "My Settings Backup",
"data_version": 1,
"data_size_bytes": 156,
"created_at": "2025-01-15T12:00:00Z"
}
}
Update Backup
PUT /api/extension/backup
Authorization: Bearer <access_token>
Content-Type: application/json
{
"backupId": "uuid",
"backupName": "Updated Name",
"data": { "settings": { "theme": "light" } },
"dataVersion": 2
}
Delete Backup
DELETE /api/extension/backup?id=<uuid> Authorization: Bearer <access_token>
Error Responses
All endpoints return errors in a consistent format:
{
"success": false,
"error": "Error message",
"requiresReauth": false
}
Common Error Codes
| HTTP Status | Error | Action |
|---|---|---|
| 401 | Invalid or expired token | Re-authenticate |
| 401 | Token expired | Call /api/extension/refresh |
| 403 | No longer a team member | Contact team admin |
| 403 | Subscription expired | Renew subscription |
| 403 | Device deactivated | Re-activate from dashboard |
| 400 | Backup too large | Reduce backup size (max 5 MB) |
| 400 | Storage quota exceeded | Delete old backups (max 50 MB total) |
| 400 | Maximum backup count reached | Delete old backups (max 20) |
Handling requiresReauth
When any API returns requiresReauth: true:
- Clear stored tokens
- Show the user a re-activation prompt
- Direct them to generate a new activation token from the dashboard
Rate Limits
API endpoints are rate-limited to prevent abuse:
| Endpoint Type | Limit |
|---|---|
| Activation | 10 per hour |
| Refresh | 20 per hour |
| Heartbeat | 100 per hour |
| Backup operations | 60 per hour |
CORS
The API accepts requests from:
- Browser extensions (
chrome-extension://,moz-extension://,safari-web-extension://) - snutils.com and approved domains
- Local development (
localhost:3000,localhost:3001)
Token Payload Structure
Activation Token
{
"type": "activation",
"userId": "uuid",
"accountId": "uuid",
"accountSlug": "team-name",
"email": "user@example.com",
"iat": 1704700000,
"exp": 1704700300
}
Access Token
{
"type": "access",
"userId": "uuid",
"accountId": "uuid",
"accountSlug": "team-name",
"deviceId": "uuid",
"deviceFingerprint": "unique-id",
"iat": 1704700000,
"exp": 1705304800
}
Refresh Token
{
"type": "refresh",
"userId": "uuid",
"accountId": "uuid",
"deviceId": "uuid",
"deviceFingerprint": "unique-id",
"iat": 1704700000,
"exp": 1707292000
}