Skip to content

Authentication ​

Learn how to authenticate with the MsGine API using API keys.

Overview ​

All MsGine API requests must be authenticated using an API key passed in the X-Api-Key header:

http
X-Api-Key: YOUR_API_KEY

Getting Your API Key ​

  1. Sign in to your MsGine account
  2. Navigate to the Developer Dashboard
  3. Click Generate New Token
  4. Copy the key immediately (it won't be shown again)
  5. Store it securely in your environment variables

A key looks like this:

msg_84f78e9a0680ebe9_ba0e01b3d623f051c8fd76299e7f8fe60cb8320e
     └──── key id ───┘ └──────────────── secret ─────────────┘

The middle segment is the key id (kid), which identifies the key and is safe to log. The final segment is the secret half; it is stored hashed and cannot be recovered after creation. Send the whole string in the header.

Scopes ​

Each key carries a set of scopes. A request to an endpoint whose scope the key does not hold returns 403.

ScopeGrants
SEND_SMSPOST /developers/sms
SEND_EMAILPOST /developers/email
SEND_PUSHPOST /developers/push and device management
SEND_WHATSAPPWhatsApp sending
READ_MESSAGESMessage history and lookup by id
READ_CONTACTSReading contacts
MANAGE_CONTACTSCreating and updating contacts
MANAGE_WEBHOOKSWebhook registration, secrets and delivery history
FULL_ACCESSEvery scope, including ones added later

Scope names are case-sensitive and validated when the key is created; an unrecognised name returns 400.

Sandbox keys ​

A key is created in one of two environments:

EnvironmentBehaviour
productionMessages are sent and the account is charged
sandboxRequests are validated and recorded; nothing is sent or charged

Sandbox responses carry "sandbox": true and "cost": 0, and the message is stored with status sent so history and lookup behave normally. Message webhooks do not fire for sandbox traffic.

Choose the environment when creating the key, or change it later:

bash
curl -X PUT https://api.msgine.net/api/v1/developer/keys/$KEY_ID \
  -H "Content-Type: application/json" \
  -d '{"environment": "sandbox"}'

HMAC request signing ​

A key may optionally require every request to be signed. Enable it at creation with "enableHmac": true; the signing secret is returned once, alongside the key.

Signed requests carry two extra headers:

x-timestamp: 1771324215
x-signature: 4f2c...9b

The signature covers the method, path, timestamp and a hash of the raw body:

message   = HTTP_METHOD + REQUEST_PATH + TIMESTAMP + SHA256_HEX(raw_body)
signature = HMAC_SHA256(signing_secret, message)

REQUEST_PATH includes the /api/v1 prefix, for example /api/v1/developers/sms. Timestamps more than 120 seconds from server time are rejected, and a signature may not be reused.

python
import hashlib, hmac, json, time

body = json.dumps({"to": "+256700000000", "message": "Hello"})
ts = str(int(time.time()))
body_hash = hashlib.sha256(body.encode()).hexdigest()

message = "POST" + "/api/v1/developers/sms" + ts + body_hash
signature = hmac.new(secret.encode(), message.encode(), hashlib.sha256).hexdigest()

headers = {
    "x-api-key": api_key,
    "x-timestamp": ts,
    "x-signature": signature,
    "Content-Type": "application/json",
}

Sign the exact bytes sent as the request body.

Security Warning

Never expose your API key in:

  • Client-side code (browser JavaScript)
  • Public repositories
  • Version control systems
  • Log files or error messages

Always store keys in environment variables or secure secret management systems.

Using API Keys ​

HTTP Requests ​

Include the key in the X-Api-Key header:

bash
curl -X POST https://api.msgine.net/api/v1/developers/sms \
  -H "X-Api-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "to": "+256701521269",
    "message": "Hello from MsGine!"
  }'

JavaScript/TypeScript ​

javascript
const response = await fetch('https://api.msgine.net/api/v1/developers/sms', {
  method: 'POST',
  headers: {
    'X-Api-Key': process.env.MSGINE_API_KEY,
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    to: '+256701521269',
    message: 'Hello from MsGine!'
  })
})

Python ​

python
import os
import requests

headers = {
    'X-Api-Key': os.environ['MSGINE_API_KEY'],
    'Content-Type': 'application/json'
}

response = requests.post(
    'https://api.msgine.net/api/v1/developers/sms',
    headers=headers,
    json={
        'to': '+256701521269',
        'message': 'Hello from MsGine!'
    }
)

PHP ​

php
<?php
$apiKey = getenv('MSGINE_API_KEY');

$ch = curl_init('https://api.msgine.net/api/v1/developers/sms');
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    "X-Api-Key: $apiKey",
    'Content-Type: application/json'
]);

Using the Official SDK ​

The SDK handles authentication automatically:

typescript
import { MsGineClient } from '@msgine/sdk'

const client = new MsGineClient({
  apiKey: process.env.MSGINE_API_KEY!,
})

// SDK automatically includes the key in all requests
await client.sms.send({
  to: '+256701521269',
  message: 'Hello!'
})

Token Management ​

Creating Tokens ​

You can create multiple API keys for different purposes:

  • Development: For testing and development
  • Production: For live applications
  • CI/CD: For automated deployments
  • Third-party: For external integrations

Each key can be revoked independently without affecting others.

Revoking Tokens ​

If a key is compromised:

  1. Navigate to the Developer Dashboard
  2. Find the compromised key
  3. Click Revoke
  4. Generate a new key
  5. Update your application with the new key

TIP

Rotate API keys regularly as a security best practice.

Authentication Errors ​

401 Unauthorized ​

Occurs when the API key is invalid or missing.

json
{
  "statusCode": 401,
  "error": "unauthorized",
  "message": "Invalid API key"
}

Check that the key is correct, has not been revoked, and is sent in the x-api-key header. A key with HMAC enabled also returns 401 when the x-signature or x-timestamp header is missing.

403 Forbidden ​

Occurs when the key lacks required permissions.

json
{
  "statusCode": 403,
  "error": "forbidden",
  "message": "Insufficient scopes. Required one of: SEND_SMS, FULL_ACCESS"
}

The message names the scopes that would have satisfied the request. Scopes can be changed on an existing key without rotating it.

A 403 is also returned when the key has an IP allowlist and the request came from an address outside it.

Security Best Practices ​

1. Use Environment Variables ​

Never hardcode keys in your source code:

typescript
// ❌ Bad
const client = new MsGineClient({
  apiKey: 'msgine_live_abc123...'
})

// ✅ Good
const client = new MsGineClient({
  apiKey: process.env.MSGINE_API_KEY!,
})

2. Server-Side Only ​

Only use API keys in server-side code:

typescript
// ✅ Good - Server code only
import { MsGineClient } from '@msgine/sdk'
const client = new MsGineClient({
  apiKey: process.env.MSGINE_API_KEY!,
})

3. Key Rotation ​

Rotate keys regularly:

  • Production keys: Every 90 days
  • Development keys: As needed
  • Immediately: If a key is compromised

4. Secure Storage ​

  • Development: .env files (add to .gitignore)
  • Production: Environment variables, secret managers (AWS Secrets Manager, Azure Key Vault, etc.)
  • CI/CD: Encrypted secrets

Testing Authentication ​

Test your authentication setup:

bash
curl https://api.msgine.net/api/v1/account \
  -H "X-Api-Key: YOUR_API_KEY"

Successful response:

json
{
  "id": "acc_1234567890",
  "email": "user@example.com",
  "balance": 5000,
  "currency": "UGX"
}

Next Steps ​

Released under the MIT License.