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:

TokenLifetimePurpose
Activation Token5 minutesOne-time device activation
Access Token7 daysAPI authentication
Refresh Token30 daysRenew 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, snippets
  • id — 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 StatusErrorAction
401Invalid or expired tokenRe-authenticate
401Token expiredCall /api/extension/refresh
403No longer a team memberContact team admin
403Subscription expiredRenew subscription
403Device deactivatedRe-activate from dashboard
400Backup too largeReduce backup size (max 5 MB)
400Storage quota exceededDelete old backups (max 50 MB total)
400Maximum backup count reachedDelete old backups (max 20)

Handling requiresReauth

When any API returns requiresReauth: true:

  1. Clear stored tokens
  2. Show the user a re-activation prompt
  3. Direct them to generate a new activation token from the dashboard

Rate Limits

API endpoints are rate-limited to prevent abuse:

Endpoint TypeLimit
Activation10 per hour
Refresh20 per hour
Heartbeat100 per hour
Backup operations60 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
}