SovereignCommand API

๐Ÿ“– 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

Free

Self-Hosted

$0/mo

Run on your own infrastructure. Full source access.

  • 100 API calls/day
  • Self-hosted only
  • Community support
  • All core features
  • Source code access (MIT)
Enterprise

On-Premise

$499/mo

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

GET /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

GET /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

GET /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

GET /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

GET /drafts

List all drafts (queued, pending, sent, cancelled). Query params: state, limit, offset.

POST /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)

POST /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

POST /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

POST /connect

Initiate WhatsApp pairing. Returns pairing code. Body:

{
  "channel_type": "whatsapp",
  "display_name": "My WhatsApp",
  "phone_number": "61419000033"
}

Draft Queue

GET /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

TierRequests/DayBurstConcurrent Streams
Free1005/min1
Pro10,000100/min5
EnterpriseUnlimited1000/min50

Error Codes

CodeHTTPDescription
UNAUTHORIZED401Invalid or missing API key
FORBIDDEN403Key lacks permission for this tier
RATE_LIMITED429Exceeded rate limit (retry-after header)
NOT_FOUND404Resource not found
VALIDATION_ERROR400Invalid request body
INTERNAL_ERROR500Server error (check logs)
CHANNEL_DISCONNECTED503WhatsApp bridge not connected
REFLECTION_FAILED500Ollama inference error