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