๐ Overview
SovereignCommand exposes a REST API (proxied via Cloudflare Worker) and a native gRPC interface for programmatic access to your sovereign messaging infrastructure. All endpoints are authenticated via API keys managed through the dashboard.
Base URL: https://sovereign-api.ml-nightworx.io/api/v1
gRPC Endpoint: sovereign-grpc.ml-nightworx.io:443 (TLS)
Authentication: Bearer token in Authorization header or X-API-Key header.
๐ API Key Management
Create, list, verify, and revoke API keys. Keys are scoped to your SovereignCommand instance and inherit your tier's rate limits.
Interactive Key Manager
๐ฐ Pricing Tiers
Self-Hosted
Run on your own infrastructure. Full source access.
- 100 API calls/day
- Self-hosted only
- Community support
- All core features
- Source code access (MIT)
Managed Cloud
We run it. You own the data. Zero ops burden.
- 10,000 API calls/day
- Managed Cloudflare Workers
- Email support (24h SLA)
- Auto-scaling infrastructure
- Custom domain included
- API key management UI
On-Premise
Full sovereignty. Your hardware, your rules.
- Unlimited API calls
- On-premise deployment
- Dedicated support channel
- SLA with penalties
- Custom integrations
- Air-gapped deployment option
- Source code escrow
๐ REST Endpoints
All REST endpoints return JSON. Errors follow the format: {"error": "message", "code": "ERROR_CODE"}.
Health & Metadata
/health
Returns service health status and version info. No auth required.
{
"status": "healthy",
"version": "1.0.0",
"uptime_seconds": 3600,
"node": "sovereign-node-1"
}
Contacts
/contacts
List all contacts. Query params: limit (default 50), offset (default 0).
[
{
"id": "contact_abc123",
"display_name": "John Doe",
"channel_id": "channel_xyz",
"created_at": "2024-01-15T10:30:00Z",
"updated_at": "2024-01-20T14:22:00Z"
}
]
Messages
/messages
List messages. Query params: contact_id (required), limit, offset, direction (inbound|outbound), since (ISO timestamp).
[
{
"id": "msg_abc123",
"contact_id": "contact_xyz",
"channel_id": "channel_123",
"direction": "inbound",
"body_text": "Hello!",
"status": "delivered",
"timestamp": "2024-01-20T14:22:00Z",
"meta": { "sender_address": "61419000033@s.whatsapp.net" }
}
]
Channels
/channels
List connected channels (WhatsApp, future: Telegram, SMS, Email).
[
{
"id": "channel_abc",
"channel_type": "whatsapp",
"display_name": "My WhatsApp",
"status": "connected",
"last_synced_at": "2024-01-20T14:20:00Z"
}
]
Drafts
/drafts
List all drafts (queued, pending, sent, cancelled). Query params: state, limit, offset.
/drafts
Enqueue a new draft for later sending (after cool-down). Body:
{
"channel_id": "channel_abc",
"contact_id": "contact_xyz",
"body_text": "Your drafted reply",
"cooldown_ms": 300000
}
Reflect (AI Analysis)
/reflect
Run local AI reflection on a message (tone analysis, translation, draft generation). Body:
{
"message_id": "msg_abc123"
}
Response includes tone analysis, emotional score (0-100), translation (if non-English), and 3 draft options with different tones.
Send Message
/send
Send a message immediately (bypasses cool-down if override). Body:
{
"channel_id": "channel_abc",
"to_address": "61419000033@s.whatsapp.net",
"body_text": "Message to send"
}
Connect Channel
/connect
Initiate WhatsApp pairing. Returns pairing code. Body:
{
"channel_type": "whatsapp",
"display_name": "My WhatsApp",
"phone_number": "61419000033"
}
Draft Queue
/draft-queue
Get drafts currently in cool-down with real-time remaining time. Includes cooldown_until (ISO timestamp) and remaining_ms.
๐ง gRPC RPCs (18 Methods)
Native gRPC interface for high-performance, streaming-capable access. Defined in proto/sovereign/v1/sovereign.proto.
UNARY Health
Health check. Returns HealthResponse with status, version, uptime.
UNARY ListChannels
List all configured channels with connection status.
UNARY ListContacts
List contacts with pagination. Request: page_size, page_token.
UNARY ListMessages
List messages for a contact. Request: contact_id, page_size, page_token, direction_filter.
UNARY SearchMessages
Full-text search across messages. Request: query, contact_id (optional), limit.
UNARY SendMessage
Send a message. Request: channel_id, to_address, body_text.
UNARY ConnectChannel
Initiate channel pairing. Returns pairing code + QR code data.
UNARY ReflectMessage
AI reflection on a message. Returns tone, emotional score, translation, draft options.
UNARY TranslateMessage
Translate a message to target language using local Ollama.
UNARY GetDraftQueue
Get drafts in cool-down with real-time timers.
UNARY OverrideCooldown
Force-send a draft immediately, bypassing cool-down.
UNARY ListDrafts
List all drafts with filtering by state.
UNARY EnqueueDraft
Add a draft to the queue with cool-down.
UNARY CancelDraft
Cancel a pending/queued draft.
STREAMING StreamEvents
Server-sent events stream: new messages, draft state changes, channel status, reflect completions.
๐ป Code Examples
List Contacts
curl -H "Authorization: Bearer YOUR_API_KEY" \
https://sovereign-api.ml-nightworx.io/api/v1/contacts
Reflect on Message
curl -X POST \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"message_id": "msg_abc123"}' \
https://sovereign-api.ml-nightworx.io/api/v1/reflect
Send Message
curl -X POST \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"channel_id": "ch_123", "to_address": "61419000033@s.whatsapp.net", "body_text": "Hello!"}' \
https://sovereign-api.ml-nightworx.io/api/v1/send
Stream Events (SSE)
curl -H "Authorization: Bearer YOUR_API_KEY" \
-H "Accept: text/event-stream" \
https://sovereign-api.ml-nightworx.io/api/v1/events/stream
Python Client
import httpx
class SovereignClient:
def __init__(self, api_key: str, base_url: str = "https://sovereign-api.ml-nightworx.io/api/v1"):
self.client = httpx.AsyncClient(
base_url=base_url,
headers={"Authorization": f"Bearer {api_key}"},
timeout=30.0
)
async def health(self):
return (await self.client.get("/health")).json()
async def list_contacts(self, limit=50, offset=0):
return (await self.client.get("/contacts", params={"limit": limit, "offset": offset})).json()
async def reflect(self, message_id: str):
return (await self.client.post("/reflect", json={"message_id": message_id})).json()
async def send(self, channel_id: str, to_address: str, body: str):
return (await self.client.post("/send", json={
"channel_id": channel_id,
"to_address": to_address,
"body_text": body
})).json()
async def stream_events(self):
async with self.client.stream("GET", "/events/stream", headers={"Accept": "text/event-stream"}) as resp:
async for line in resp.aiter_lines():
if line.startswith("data: "):
yield json.loads(line[6:])
# Usage
async def main():
client = SovereignClient("sk_live_...")
health = await client.health()
print(f"Status: {health['status']}")
contacts = await client.list_contacts()
for c in contacts:
print(f"{c['display_name']}: {c['id']}")
# Reflect on latest message
if contacts:
msgs = await client.list_messages(contacts[0]['id'])
if msgs:
reflection = await client.reflect(msgs[0]['id'])
print(f"Tone: {reflection.get('tone_analysis')}")
print(f"Score: {reflection.get('emotional_score')}")
if __name__ == "__main__":
import asyncio, json
asyncio.run(main())
JavaScript / TypeScript
class SovereignClient {
constructor(apiKey, baseUrl = 'https://sovereign-api.ml-nightworx.io/api/v1') {
this.baseUrl = baseUrl;
this.headers = {
'Authorization': `Bearer ${apiKey}`,
'Content-Type': 'application/json'
};
}
async request(path, options = {}) {
const res = await fetch(`${this.baseUrl}${path}`, {
...options,
headers: { ...this.headers, ...options.headers }
});
if (!res.ok) {
const err = await res.json().catch(() => ({ error: res.statusText }));
throw new Error(err.error || `HTTP ${res.status}`);
}
return res.json();
}
health() { return this.request('/health'); }
listContacts(params = {}) {
const qs = new URLSearchParams(params).toString();
return this.request(`/contacts?${qs}`);
}
listMessages(contactId, params = {}) {
const qs = new URLSearchParams({ contact_id: contactId, ...params }).toString();
return this.request(`/messages?${qs}`);
}
reflect(messageId) {
return this.request('/reflect', { method: 'POST', body: JSON.stringify({ message_id: messageId }) });
}
send(channelId, toAddress, bodyText) {
return this.request('/send', {
method: 'POST',
body: JSON.stringify({ channel_id: channelId, to_address: toAddress, body_text: bodyText })
});
}
*streamEvents() {
const evtSource = new EventSource(`${this.baseUrl}/events/stream`, {
headers: { 'Authorization': this.headers.Authorization }
});
evtSource.onmessage = (e) => { yield JSON.parse(e.data); };
}
}
// Usage
const client = new SovereignClient('sk_live_...');
const health = await client.health();
console.log('Status:', health.status);
const contacts = await client.listContacts({ limit: 10 });
for (const c of contacts) {
console.log(`${c.display_name}: ${c.id}`);
}
Go Client
package sovereign
import (
"bytes"
"context"
"encoding/json"
"fmt"
"io"
"net/http"
"time"
)
type Client struct {
httpClient *http.Client
baseURL string
apiKey string
}
type HealthResponse struct {
Status string `json:"status"`
Version string `json:"version"`
UptimeSecs int `json:"uptime_seconds"`
Node string `json:"node"`
}
type Contact struct {
ID string `json:"id"`
DisplayName string `json:"display_name"`
ChannelID string `json:"channel_id"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
type ReflectRequest struct {
MessageID string `json:"message_id"`
}
type ReflectResponse struct {
ToneAnalysis string `json:"tone_analysis"`
EmotionalScore int `json:"emotional_score"`
TranslatedBody string `json:"translated_body"`
Options []Option `json:"options"`
DraftID string `json:"draft_id"`
}
type Option struct {
Label string `json:"label"`
Body string `json:"body"`
}
func NewClient(apiKey string) *Client {
return &Client{
httpClient: &http.Client{Timeout: 30 * time.Second},
baseURL: "https://sovereign-api.ml-nightworx.io/api/v1",
apiKey: apiKey,
}
}
func (c *Client) doReq(ctx context.Context, method, path string, body, dest any) error {
var buf io.Reader
if body != nil {
b, _ := json.Marshal(body)
buf = bytes.NewReader(b)
}
req, _ := http.NewRequestWithContext(ctx, method, c.baseURL+path, buf)
req.Header.Set("Authorization", "Bearer "+c.apiKey)
req.Header.Set("Content-Type", "application/json")
resp, err := c.httpClient.Do(req)
if err != nil { return err }
defer resp.Body.Close()
if resp.StatusCode >= 400 {
b, _ := io.ReadAll(resp.Body)
return fmt.Errorf("HTTP %d: %s", resp.StatusCode, string(b))
}
if dest != nil {
return json.NewDecoder(resp.Body).Decode(dest)
}
return nil
}
func (c *Client) Health(ctx context.Context) (*HealthResponse, error) {
var resp HealthResponse
err := c.doReq(ctx, "GET", "/health", nil, &resp)
return &resp, err
}
func (c *Client) ListContacts(ctx context.Context, limit, offset int) ([]Contact, error) {
path := fmt.Sprintf("/contacts?limit=%d&offset=%d", limit, offset)
var resp []Contact
err := c.doReq(ctx, "GET", path, nil, &resp)
return resp, err
}
func (c *Client) Reflect(ctx context.Context, messageID string) (*ReflectResponse, error) {
var resp ReflectResponse
err := c.doReq(ctx, "POST", "/reflect", ReflectRequest{MessageID: messageID}, &resp)
return &resp, err
}
// Usage
func main() {
ctx := context.Background()
client := sovereign.NewClient("sk_live_...")
health, _ := client.Health(ctx)
fmt.Printf("Status: %s v%s\n", health.Status, health.Version)
contacts, _ := client.ListContacts(ctx, 10, 0)
for _, c := range contacts {
fmt.Printf("%s: %s\n", c.DisplayName, c.ID)
}
}
โก Rate Limits & Errors
| Tier | Requests/Day | Burst | Concurrent Streams |
|---|---|---|---|
| Free | 100 | 5/min | 1 |
| Pro | 10,000 | 100/min | 5 |
| Enterprise | Unlimited | 1000/min | 50 |
Error Codes
| Code | HTTP | Description |
|---|---|---|
UNAUTHORIZED | 401 | Invalid or missing API key |
FORBIDDEN | 403 | Key lacks permission for this tier |
RATE_LIMITED | 429 | Exceeded rate limit (retry-after header) |
NOT_FOUND | 404 | Resource not found |
VALIDATION_ERROR | 400 | Invalid request body |
INTERNAL_ERROR | 500 | Server error (check logs) |
CHANNEL_DISCONNECTED | 503 | WhatsApp bridge not connected |
REFLECTION_FAILED | 500 | Ollama inference error |