Skip to content

API Keys Management ​

API keys allow you to authenticate API requests programmatically. Learn how to create, manage, and secure your API keys.

Overview ​

API keys are used for:

  • Sending messages via the API
  • Retrieving message history
  • Accessing message details

SECURITY

API keys are sensitive credentials. Never expose them in client-side code, public repositories, or logs.

Authentication Header ​

Use API keys in the X-API-Key header:

bash
X-API-Key: ybk_1234567890abcdef...

Creating API Keys ​

API keys can only be created using JWT authentication (after logging in to your workspace).

Endpoint ​

POST /api/v1/api-keys

Request ​

bash
curl -X POST https://api.yebolink.com/api/v1/api-keys \
  -H "Authorization: Bearer YOUR_JWT_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Production API Key",
    "scopes": ["send_messages", "read_messages"]
  }'
javascript
const response = await fetch('https://api.yebolink.com/api/v1/api-keys', {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${jwtToken}`,
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    name: 'Production API Key',
    scopes: ['send_messages', 'read_messages']
  })
});

const data = await response.json();
console.log('API Key:', data.data.key);
// Save this key securely - it won't be shown again!
python
import requests
import os

response = requests.post(
    'https://api.yebolink.com/api/v1/api-keys',
    headers={
        'Authorization': f'Bearer {jwt_token}',
        'Content-Type': 'application/json'
    },
    json={
        'name': 'Production API Key',
        'scopes': ['send_messages', 'read_messages']
    }
)

data = response.json()
api_key = data['data']['key']
print(f'API Key: {api_key}')
# Save this key securely - it won't be shown again!
php
<?php
$curl = curl_init();

curl_setopt_array($curl, [
    CURLOPT_URL => 'https://api.yebolink.com/api/v1/api-keys',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_POST => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . $jwtToken,
        'Content-Type: application/json'
    ],
    CURLOPT_POSTFIELDS => json_encode([
        'name' => 'Production API Key',
        'scopes' => ['send_messages', 'read_messages']
    ])
]);

$response = curl_exec($curl);
$data = json_decode($response, true);

echo 'API Key: ' . $data['data']['key'];
// Save this key securely - it won't be shown again!

curl_close($curl);
?>
ruby
require 'net/http'
require 'json'
require 'uri'

uri = URI('https://api.yebolink.com/api/v1/api-keys')
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true

request = Net::HTTP::Post.new(uri)
request['Authorization'] = "Bearer #{jwt_token}"
request['Content-Type'] = 'application/json'
request.body = {
  name: 'Production API Key',
  scopes: ['send_messages', 'read_messages']
}.to_json

response = http.request(request)
data = JSON.parse(response.body)

puts "API Key: #{data['data']['key']}"
# Save this key securely - it won't be shown again!
go
package main

import (
    "bytes"
    "encoding/json"
    "fmt"
    "io"
    "net/http"
)

type CreateKeyRequest struct {
    Name   string   `json:"name"`
    Scopes []string `json:"scopes"`
}

type CreateKeyResponse struct {
    Success bool `json:"success"`
    Data    struct {
        ID        string   `json:"id"`
        Name      string   `json:"name"`
        Key       string   `json:"key"`
        KeyPrefix string   `json:"key_prefix"`
        Scopes    []string `json:"scopes"`
        CreatedAt string   `json:"created_at"`
        Warning   string   `json:"warning"`
    } `json:"data"`
}

func createAPIKey(jwtToken string) (string, error) {
    reqBody := CreateKeyRequest{
        Name:   "Production API Key",
        Scopes: []string{"send_messages", "read_messages"},
    }

    jsonData, _ := json.Marshal(reqBody)

    req, _ := http.NewRequest("POST",
        "https://api.yebolink.com/api/v1/api-keys",
        bytes.NewBuffer(jsonData))

    req.Header.Set("Authorization", "Bearer "+jwtToken)
    req.Header.Set("Content-Type", "application/json")

    client := &http.Client{}
    resp, err := client.Do(req)
    if err != nil {
        return "", err
    }
    defer resp.Body.Close()

    body, _ := io.ReadAll(resp.Body)

    var result CreateKeyResponse
    json.Unmarshal(body, &result)

    fmt.Println("API Key:", result.Data.Key)
    // Save this key securely - it won't be shown again!

    return result.Data.Key, nil
}

Response ​

json
{
  "success": true,
  "data": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Production API Key",
    "key": "ybk_1234567890abcdef1234567890abcdef1234567890abcdef",
    "key_prefix": "ybk_1234567...",
    "scopes": ["send_messages", "read_messages"],
    "created_at": "2025-11-02T12:00:00Z",
    "warning": "Save this key securely. It will not be shown again."
  }
}

IMPORTANT

The full API key is only shown once when created. Save it immediately in a secure location. If you lose it, you'll need to create a new one.

Parameters ​

ParameterTypeRequiredDescription
namestringYesDescriptive name for the API key
scopesarrayNoPermissions for the key (defaults to all)

Available Scopes ​

ScopeDescription
send_messagesSend messages via API
read_messagesRetrieve message history and details

Listing API Keys ​

Get all API keys for your workspace.

Endpoint ​

GET /api/v1/api-keys

Request ​

bash
curl -X GET https://api.yebolink.com/api/v1/api-keys \
  -H "Authorization: Bearer YOUR_JWT_TOKEN"
javascript
const response = await fetch('https://api.yebolink.com/api/v1/api-keys', {
  headers: {
    'Authorization': `Bearer ${jwtToken}`
  }
});

const data = await response.json();
console.log('API Keys:', data.data.api_keys);
python
import requests

response = requests.get(
    'https://api.yebolink.com/api/v1/api-keys',
    headers={'Authorization': f'Bearer {jwt_token}'}
)

data = response.json()
for key in data['data']['api_keys']:
    print(f"{key['name']}: {key['key_prefix']} (Last used: {key['last_used_at']})")
php
<?php
$curl = curl_init();

curl_setopt_array($curl, [
    CURLOPT_URL => 'https://api.yebolink.com/api/v1/api-keys',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . $jwtToken
    ]
]);

$response = curl_exec($curl);
$data = json_decode($response, true);

foreach ($data['data']['api_keys'] as $key) {
    echo $key['name'] . ': ' . $key['key_prefix'] . "\n";
}

curl_close($curl);
?>

Response ​

json
{
  "success": true,
  "data": {
    "api_keys": [
      {
        "id": "550e8400-e29b-41d4-a716-446655440000",
        "name": "Production API Key",
        "key_prefix": "ybk_1234567...",
        "scopes": ["send_messages", "read_messages"],
        "is_active": true,
        "last_used_at": "2025-11-02T10:30:00Z",
        "created_at": "2025-11-01T12:00:00Z"
      },
      {
        "id": "660e8400-e29b-41d4-a716-446655440001",
        "name": "Development API Key",
        "key_prefix": "ybk_9876543...",
        "scopes": ["send_messages", "read_messages"],
        "is_active": true,
        "last_used_at": null,
        "created_at": "2025-10-15T08:00:00Z"
      }
    ]
  }
}

TIP

The last_used_at field helps you identify unused keys that can be safely deleted.


Deactivating API Keys ​

Deactivate an API key to immediately revoke access.

Endpoint ​

DELETE /api/v1/api-keys/:id

Request ​

bash
curl -X DELETE https://api.yebolink.com/api/v1/api-keys/550e8400-e29b-41d4-a716-446655440000 \
  -H "Authorization: Bearer YOUR_JWT_TOKEN"
javascript
const keyId = '550e8400-e29b-41d4-a716-446655440000';

const response = await fetch(`https://api.yebolink.com/api/v1/api-keys/${keyId}`, {
  method: 'DELETE',
  headers: {
    'Authorization': `Bearer ${jwtToken}`
  }
});

const data = await response.json();
console.log(data.data.message);
python
import requests

key_id = '550e8400-e29b-41d4-a716-446655440000'

response = requests.delete(
    f'https://api.yebolink.com/api/v1/api-keys/{key_id}',
    headers={'Authorization': f'Bearer {jwt_token}'}
)

data = response.json()
print(data['data']['message'])
php
<?php
$keyId = '550e8400-e29b-41d4-a716-446655440000';

$curl = curl_init();

curl_setopt_array($curl, [
    CURLOPT_URL => "https://api.yebolink.com/api/v1/api-keys/$keyId",
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_CUSTOMREQUEST => 'DELETE',
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . $jwtToken
    ]
]);

$response = curl_exec($curl);
$data = json_decode($response, true);

echo $data['data']['message'];

curl_close($curl);
?>

Response ​

json
{
  "success": true,
  "data": {
    "message": "API key deactivated successfully"
  }
}

WARNING

Once deactivated, the API key cannot be reactivated. You'll need to create a new key.


Best Practices ​

1. Store API Keys Securely ​

Use Environment Variables:

bash
# .env file
YEBOLINK_API_KEY=ybk_1234567890abcdef...
javascript
// Node.js
require('dotenv').config();
const apiKey = process.env.YEBOLINK_API_KEY;
python
# Python
import os
from dotenv import load_dotenv

load_dotenv()
api_key = os.getenv('YEBOLINK_API_KEY')

Never hardcode API keys:

javascript
// ❌ DON'T DO THIS
const apiKey = 'ybk_1234567890abcdef...';

// ✅ DO THIS
const apiKey = process.env.YEBOLINK_API_KEY;

2. Use Different Keys for Different Environments ​

Create separate API keys for:

  • Development
  • Staging
  • Production

This allows you to:

  • Track usage per environment
  • Rotate keys without affecting all environments
  • Deactivate compromised keys without disrupting production

3. Rotate Keys Regularly ​

javascript
// Key rotation workflow
async function rotateAPIKey(oldKeyId, jwtToken) {
  // 1. Create new API key
  const newKeyResponse = await fetch('https://api.yebolink.com/api/v1/api-keys', {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${jwtToken}`,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      name: 'Production API Key (Rotated)',
      scopes: ['send_messages', 'read_messages']
    })
  });

  const newKey = await newKeyResponse.json();

  // 2. Update your environment variables
  console.log('New API Key:', newKey.data.key);
  // Update your .env file or secret manager

  // 3. Wait for deployment to complete
  await deployNewKey(newKey.data.key);

  // 4. Deactivate old key
  await fetch(`https://api.yebolink.com/api/v1/api-keys/${oldKeyId}`, {
    method: 'DELETE',
    headers: {
      'Authorization': `Bearer ${jwtToken}`
    }
  });

  console.log('API key rotation complete');
}

4. Monitor API Key Usage ​

Check the last_used_at field regularly:

javascript
async function auditAPIKeys(jwtToken) {
  const response = await fetch('https://api.yebolink.com/api/v1/api-keys', {
    headers: {
      'Authorization': `Bearer ${jwtToken}`
    }
  });

  const data = await response.json();
  const now = new Date();
  const thirtyDaysAgo = new Date(now.setDate(now.getDate() - 30));

  data.data.api_keys.forEach(key => {
    const lastUsed = key.last_used_at ? new Date(key.last_used_at) : null;

    if (!lastUsed || lastUsed < thirtyDaysAgo) {
      console.log(`⚠️  Unused key: ${key.name} (${key.key_prefix})`);
      console.log(`   Consider deactivating if not needed`);
    }
  });
}

5. Implement Rate Limit Handling ​

javascript
class YeboLinkClient {
  constructor(apiKey) {
    this.apiKey = apiKey;
    this.baseURL = 'https://api.yebolink.com';
  }

  async request(endpoint, options = {}) {
    const response = await fetch(`${this.baseURL}${endpoint}`, {
      ...options,
      headers: {
        'X-API-Key': this.apiKey,
        'Content-Type': 'application/json',
        ...options.headers
      }
    });

    // Check rate limits
    const remaining = response.headers.get('X-RateLimit-Remaining');
    const limit = response.headers.get('X-RateLimit-Limit');

    if (parseInt(remaining) < 10) {
      console.warn(`⚠️  Rate limit warning: ${remaining}/${limit} requests remaining`);
    }

    if (response.status === 429) {
      const retryAfter = response.headers.get('Retry-After');
      throw new Error(`Rate limited. Retry after ${retryAfter} seconds`);
    }

    return response.json();
  }
}

6. Never Expose API Keys in Client-Side Code ​

javascript
// ❌ NEVER DO THIS - Exposed in browser
const sendMessage = async (to, text) => {
  const response = await fetch('https://api.yebolink.com/api/v1/messages/send', {
    method: 'POST',
    headers: {
      'X-API-Key': 'ybk_1234567890abcdef...' // EXPOSED TO USERS!
    },
    body: JSON.stringify({ to, channel: 'sms', content: { text } })
  });
};

// ✅ DO THIS - API key stays on server
// Frontend:
const sendMessage = async (to, text) => {
  const response = await fetch('/api/send-message', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ to, text })
  });
};

// Backend (Node.js/Express):
app.post('/api/send-message', async (req, res) => {
  const { to, text } = req.body;

  const response = await fetch('https://api.yebolink.com/api/v1/messages/send', {
    method: 'POST',
    headers: {
      'X-API-Key': process.env.YEBOLINK_API_KEY // Safe on server
    },
    body: JSON.stringify({
      to,
      channel: 'sms',
      content: { text }
    })
  });

  const data = await response.json();
  res.json(data);
});

Security Checklist ​

  • [ ] Store API keys in environment variables
  • [ ] Never commit API keys to version control
  • [ ] Use different keys for dev/staging/prod
  • [ ] Rotate keys every 90 days
  • [ ] Monitor key usage regularly
  • [ ] Deactivate unused keys
  • [ ] Never expose keys in client-side code
  • [ ] Implement proper error handling
  • [ ] Use HTTPS for all API requests
  • [ ] Log API key usage for audit trails

Troubleshooting ​

"Invalid API key" Error ​

Possible causes:

  1. API key is incorrect or malformed
  2. API key has been deactivated
  3. Wrong authentication header

Solution:

javascript
// Verify API key format
console.log('API Key starts with "ybk_":', apiKey.startsWith('ybk_'));

// Check key is active
const response = await fetch('https://api.yebolink.com/api/v1/api-keys', {
  headers: {
    'Authorization': `Bearer ${jwtToken}`
  }
});

const data = await response.json();
const activeKeys = data.data.api_keys.filter(k => k.is_active);
console.log('Active keys:', activeKeys.map(k => k.key_prefix));

Rate Limit Exceeded ​

Solution:

javascript
async function sendWithBackoff(messageData, maxRetries = 3) {
  for (let i = 0; i < maxRetries; i++) {
    const response = await fetch('https://api.yebolink.com/api/v1/messages/send', {
      method: 'POST',
      headers: {
        'X-API-Key': process.env.YEBOLINK_API_KEY,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify(messageData)
    });

    if (response.status === 429) {
      const delay = Math.pow(2, i) * 1000; // Exponential backoff
      console.log(`Rate limited. Retrying in ${delay}ms...`);
      await new Promise(resolve => setTimeout(resolve, delay));
      continue;
    }

    return await response.json();
  }

  throw new Error('Max retries exceeded');
}

Need Help? ​

Built with VitePress