Skip to main content
Blnk provides two authentication methods: a master key for full access and scoped API keys for granular permissions. This guide shows you how to manage authentication and implement secure access control.

Authentication methods

Master key

The master key provides unrestricted access to all Blnk API endpoints. It’s configured in your blnk.json file:
{
  "server": {
    "secret_key": "your-secure-master-key-here",
    "secure": true
  }
}
Keep your master key secret! Never commit it to version control or share it publicly.

API keys

API keys provide scoped access with specific permissions. They’re ideal for:
  • Third-party integrations
  • Mobile applications
  • Microservices
  • Client-facing APIs

Using the master key

Include the master key in the X-Blnk-Key header:
curl -X POST https://api.yourapp.com/transactions \
  -H "X-Blnk-Key: your-master-key" \
  -H "Content-Type: application/json" \
  -d '{
    "amount": 100.00,
    "currency": "USD",
    "source": "bln_source",
    "destination": "bln_dest"
  }'
The master key grants access to all endpoints.

Creating API keys

Generate scoped API keys for limited access:
1

Create API key with scopes

POST /api-keys
X-Blnk-Key: your-master-key

{
  "name": "Mobile App Key",
  "owner_id": "app_mobile_v1",
  "scopes": [
    "transactions:read",
    "transactions:write",
    "balances:read"
  ],
  "expires_at": "2025-12-31T23:59:59Z"
}
2

Response with API key

{
  "api_key_id": "key_abc123xyz",
  "key": "blnk_sk_1234567890abcdef",
  "name": "Mobile App Key",
  "owner_id": "app_mobile_v1",
  "scopes": [
    "transactions:read",
    "transactions:write",
    "balances:read"
  ],
  "expires_at": "2025-12-31T23:59:59Z",
  "created_at": "2024-01-15T10:00:00Z",
  "active": true
}
The key field is only returned once during creation. Store it securely!
3

Use API key in requests

curl -X GET https://api.yourapp.com/balances/bln_123 \
  -H "X-Blnk-Key: blnk_sk_1234567890abcdef"

Available scopes

API keys use resource:action format for permissions:

Transaction scopes

transactions:read

View transactions and transaction details

transactions:write

Create and record new transactions

Balance scopes

balances:read

View balance information

balances:write

Create new balances

Account scopes

accounts:read

View account information

accounts:write

Create and modify accounts

Ledger scopes

ledgers:read

View ledger information

ledgers:write

Create and modify ledgers

Identity scopes

identities:read

View identity information

identities:write

Create and modify identities

Monitor scopes

balance-monitors:read

View balance monitors

balance-monitors:write

Create and manage monitors

Reconciliation scopes

reconciliation:read

View reconciliation data

reconciliation:write

Start and manage reconciliations

Other scopes

hooks:read

View webhook configurations

hooks:write

Create and manage webhooks

api-keys:read

View API keys (own keys only)

api-keys:write

Create and revoke API keys

search:read

Search across resources

metadata:write

Update metadata on resources

Managing API keys

List all API keys

GET /api-keys?owner_id=app_mobile_v1
X-Blnk-Key: your-master-key
[
  {
    "api_key_id": "key_abc123xyz",
    "name": "Mobile App Key",
    "owner_id": "app_mobile_v1",
    "scopes": [
      "transactions:read",
      "balances:read"
    ],
    "expires_at": "2025-12-31T23:59:59Z",
    "created_at": "2024-01-15T10:00:00Z",
    "last_used": "2024-01-16T14:30:00Z",
    "active": true
  }
]

Revoke an API key

DELETE /api-keys/key_abc123xyz
X-Blnk-Key: your-master-key
{
  "message": "API key revoked successfully"
}
Revoked keys immediately stop working.

Common API key configurations

Read-only access

For analytics and reporting:
POST /api-keys
{
  "name": "Analytics Dashboard",
  "owner_id": "dashboard_analytics",
  "scopes": [
    "transactions:read",
    "balances:read",
    "accounts:read",
    "search:read"
  ],
  "expires_at": "2025-12-31T23:59:59Z"
}

Mobile app key

For customer-facing mobile applications:
POST /api-keys
{
  "name": "Customer Mobile App",
  "owner_id": "app_mobile_customer",
  "scopes": [
    "transactions:read",
    "transactions:write",
    "balances:read"
  ],
  "expires_at": "2025-12-31T23:59:59Z"
}

Third-party integration

For payment processor integration:
POST /api-keys
{
  "name": "Stripe Integration",
  "owner_id": "integration_stripe",
  "scopes": [
    "transactions:write",
    "transactions:read",
    "balances:read",
    "reconciliation:write"
  ],
  "expires_at": "2025-12-31T23:59:59Z"
}

Webhook management key

For managing webhooks only:
POST /api-keys
{
  "name": "Webhook Manager",
  "owner_id": "service_webhook_manager",
  "scopes": [
    "hooks:read",
    "hooks:write"
  ],
  "expires_at": "2025-12-31T23:59:59Z"
}

Automatic metadata injection

When using API keys, Blnk automatically adds the key ID to transaction metadata:
// Request with API key
POST /transactions
X-Blnk-Key: blnk_sk_abc123

{
  "amount": 100.00,
  "source": "bln_source",
  "destination": "bln_dest"
}

// Blnk automatically injects:
{
  "amount": 100.00,
  "source": "bln_source",
  "destination": "bln_dest",
  "meta_data": {
    "BLNK_GENERATED_BY": "key_abc123xyz"
  }
}
This helps with audit trails and tracking which API key created each transaction.

Implementation code (from api/middleware/auth.go:181-270)

How Blnk authenticates requests:
func (m *AuthMiddleware) Authenticate() gin.HandlerFunc {
    return func(c *gin.Context) {
        // Skip auth for health endpoints
        if c.Request.URL.Path == "/" || c.Request.URL.Path == "/health" {
            c.Next()
            return
        }

        // Check if secure mode is enabled
        conf, err := config.Fetch()
        if err == nil && conf != nil && !conf.Server.Secure {
            c.Next()
            return
        }

        key := extractKey(c)
        if key == "" {
            c.JSON(401, gin.H{"error": "Authentication required. Use X-Blnk-Key header"})
            c.Abort()
            return
        }

        // Check if it's the master key
        if err == nil && conf != nil && conf.Server.SecretKey == key {
            c.Set("isMasterKey", true)
            c.Next()
            return
        }

        // Try API key authentication
        apiKey, err := m.service.GetAPIKeyByKey(c.Request.Context(), key)
        if err != nil {
            c.JSON(401, gin.H{"error": "Invalid API key"})
            c.Abort()
            return
        }

        if !apiKey.IsValid() {
            c.JSON(401, gin.H{"error": "API key is expired or revoked"})
            c.Abort()
            return
        }

        // Determine required resource from path
        resource := getResourceFromPath(c.Request.URL.Path)
        if resource == "" {
            c.JSON(403, gin.H{"error": "Unknown resource type"})
            c.Abort()
            return
        }

        // Check if API key has permission
        if !HasPermission(apiKey.Scopes, resource, c.Request.Method) {
            action := methodToAction[c.Request.Method]
            c.JSON(403, gin.H{"error": "Insufficient permissions for " + string(resource) + ":" + string(action)})
            c.Abort()
            return
        }

        // Inject API key ID into metadata for POST requests
        if c.Request.Method == "POST" && c.Request.Body != nil {
            if err := injectAPIKeyToMetadata(c, apiKey.APIKeyID); err != nil {
                logrus.Error("Failed to inject API key ID into metadata:", err)
            }
        }

        // Update last used timestamp
        go func() {
            _ = m.service.UpdateLastUsed(c.Request.Context(), apiKey.APIKeyID)
        }()

        c.Set("apiKey", apiKey)
        c.Next()
    }
}

Disabling authentication

For development or internal deployments, you can disable authentication:
{
  "server": {
    "secure": false
  }
}
Never disable authentication in production! This exposes your entire API.

Best practices

Rotate keys regularly

Create new API keys and revoke old ones periodically

Use least privilege

Grant only the minimum scopes needed for each integration

Set expiry dates

Always set expiration dates for API keys

Monitor key usage

Track last_used timestamps to identify unused keys

Revoke immediately

Revoke compromised keys immediately

Store securely

Use environment variables or secret managers for keys

Handling authentication errors

Missing authentication

{
  "error": "Authentication required. Use X-Blnk-Key header"
}
Solution: Include X-Blnk-Key header in all requests.

Invalid API key

{
  "error": "Invalid API key"
}
Solution: Verify the key is correct and hasn’t been revoked.

Expired API key

{
  "error": "API key is expired or revoked"
}
Solution: Create a new API key or extend the expiration date.

Insufficient permissions

{
  "error": "Insufficient permissions for transactions:write"
}
Solution: Update the API key scopes or use a key with appropriate permissions.

Environment-specific keys

Use different API keys for each environment:
# Development
export BLNK_API_KEY="blnk_sk_dev_1234567890"

# Staging
export BLNK_API_KEY="blnk_sk_staging_abcdefgh"

# Production
export BLNK_API_KEY="blnk_sk_prod_xyz7890abc"

SDK authentication examples

Node.js

const axios = require('axios');

const blnkClient = axios.create({
  baseURL: 'https://api.yourapp.com',
  headers: {
    'X-Blnk-Key': process.env.BLNK_API_KEY
  }
});

// Use the client
const response = await blnkClient.post('/transactions', {
  amount: 100.00,
  source: 'bln_source',
  destination: 'bln_dest'
});

Python

import os
import requests

class BlnkClient:
    def __init__(self):
        self.base_url = 'https://api.yourapp.com'
        self.headers = {
            'X-Blnk-Key': os.environ['BLNK_API_KEY']
        }
    
    def create_transaction(self, data):
        response = requests.post(
            f'{self.base_url}/transactions',
            json=data,
            headers=self.headers
        )
        return response.json()

# Use the client
client = BlnkClient()
result = client.create_transaction({
    'amount': 100.00,
    'source': 'bln_source',
    'destination': 'bln_dest'
})

Go

package main

import (
    "bytes"
    "encoding/json"
    "net/http"
    "os"
)

type BlnkClient struct {
    BaseURL string
    APIKey  string
    Client  *http.Client
}

func NewBlnkClient() *BlnkClient {
    return &BlnkClient{
        BaseURL: "https://api.yourapp.com",
        APIKey:  os.Getenv("BLNK_API_KEY"),
        Client:  &http.Client{},
    }
}

func (c *BlnkClient) CreateTransaction(data map[string]interface{}) error {
    body, _ := json.Marshal(data)
    
    req, _ := http.NewRequest("POST", c.BaseURL+"/transactions", bytes.NewBuffer(body))
    req.Header.Set("X-Blnk-Key", c.APIKey)
    req.Header.Set("Content-Type", "application/json")
    
    resp, err := c.Client.Do(req)
    if err != nil {
        return err
    }
    defer resp.Body.Close()
    
    return nil
}

Security checklist

  • Store master key in secure environment variable
  • Never commit API keys to version control
  • Use HTTPS for all API requests
  • Set expiration dates on all API keys
  • Revoke unused or compromised keys immediately
  • Monitor API key usage via last_used timestamps
  • Use scoped API keys instead of master key when possible
  • Implement rate limiting on your endpoints
  • Log authentication failures for security monitoring
  • Rotate API keys regularly (every 90 days recommended)

Next steps

Webhooks

Secure your webhook endpoints with API keys

Wallet Management

Start building with authenticated API requests

Build docs developers (and LLMs) love