logicspike/docs

Contact Intelligence

API Specification — Contact Intelligence

Last Updated: 2026-06-29 Status: Active


1. Service Overview

Property Value
Service Owner apps/contact-intelligence
Base URL https://api.logicspike.com/contact-intel (via Gateway)
Internal Microservice contact-intelligence.logicspike.workers.dev
Gateway Key Header x-gateway-key
Primary Consumer Chat Engine (service-to-service)
Secondary Consumer AI Brain (via brain tools)

2. Authentication Requirements

Domain Auth Type Header Consumer
Context Retrieval Gateway key x-gateway-key Chat Engine
Ingestion Gateway key x-gateway-key Chat Engine
Contact Management JWT (user session) Authorization: Bearer <token> AI Brain / Dashboard
Outreach Management JWT (user session) Authorization: Bearer <token> AI Brain / Dashboard
Internal (Cron, Queues) Gateway key x-gateway-key CF Cron / CF Queues

3. Endpoints by Domain

3.1 Context & Ingestion (Chat Engine — Hot Path)

Get Contact Context

The primary hot-path endpoint. Called by Chat Engine before generating a response. Returns the L1+L2 context packet: behavioral guidance, message relevance, relationship state, crisis level, and pending outreach triggers.

GET /contact-intel/context/:contact_id
x-gateway-key: <key>

Query Parameters:

Param Type Default Description
session_count Integer 0 Number of sessions this contact has had. Determines cold-start vs. real context.
message String ≤500 Current user message. When present, triggers L2 semantic search and entity expansion.
bot_id String Bot identifier. Used to fetch a bot-specific cold-start brief from Chat Engine (Phase 5).

Response 200:

{
  "session_core": "Arjun is a software engineer who chats late at night. He has a dog named Bruno. He appreciates humor but needs direct support during work stress.",
  "message_relevance": "Relevant: Bruno had a vet appointment last week. Also known: Infosys, sprint deadline",
  "has_message_context": true,
  "relationship": {
    "stage": "established",
    "trajectory": "moving_up",
    "velocity": "moderate",
    "mood_trend": "stable"
  },
  "crisis_level": "none",
  "triggers": [
    {
      "triggerType": "inactivity",
      "message": "Hey! It's been a while — how's Bruno doing?",
      "context": { "days_inactive": 5 }
    }
  ]
}

Field Reference:

Field Type Description
session_core string | null L1 behavioral guidance. Generic cold-start text when session_count < cold_start_used_until (default 5). contacts.core_summary for established contacts; null until first weekly consolidation runs.
message_relevance string | null L2 result: most relevant memory + entity neighbors for the current message. Null if no message param or no semantic hits above 0.7 cosine similarity.
has_message_context boolean Whether L2 found anything useful for this message.
relationship.stage enum new | building | established | deep | fading | dormant
relationship.trajectory enum | null moving_up | stable | moving_down | volatile — from latest weekly relationship snapshot
relationship.velocity enum | null slow | moderate | fast — rate of relationship change
relationship.mood_trend enum | null improving | declining | stable | volatile — from latest weekly emotional snapshot
crisis_level enum none | distress | crisis — gates CE safety escalation
triggers array Pending outreach triggers whose scheduled_at has passed. Max 3. Empty array when none.

Backward-Compat Mood Endpoint (remove in Phase 5):

GET /contact-intel/context/:contact_id/mood

Returns the legacy { mood, mood_line, energy, relationship_stage, ... } shape. Chat Engine still calls this until the Phase 5 orchestrator update. Do not use for new consumers.

Error Codes:

Code HTTP Description
CONTACT_NOT_FOUND 404 Contact ID does not exist for this tenant

Performance SLA: < 30ms p95 (5 parallel queries)


Ingest Interaction

Called by Chat Engine on every message. Runs mood classification, rules-based memory extraction, entity detection, and contact state update synchronously before returning 202. When session_close: true, also runs the LLM batch session-close extraction inline.

POST /contact-intel/ingest
x-gateway-key: <key>
Content-Type: application/json

Request Body:

{
  "contact_id": "ct_arjun",
  "conversation_id": "conv_abc123",
  "message": "Bruno ate my shoes again",
  "role": "user",
  "session_close": false,
  "session_id": "sess_xyz",
  "session_messages": []
}
Field Type Required Description
contact_id UUID Contact to ingest for
message String The message text
role Enum user or assistant. Mood + memory extraction only run for user messages.
conversation_id UUID CE conversation ID. Stored as source_id on extracted memories.
session_close Boolean Default false. When true, triggers the LLM batch extraction (requires session_messages).
session_id String CE session ID. Stored as source_session_id on LLM-extracted memories for provenance.
session_messages Array Full transcript [{ role, content }]. Required when session_close: true.

Response 202:

{
  "status": "accepted",
  "memories_extracted": 2,
  "entities_extracted": 1,
  "mood": { "mood": "frustrated", "confidence": 0.88, "energy": "medium" },
  "contact_id": "ct_arjun"
}

Note: memories_extracted counts only inline rules-based extractions, not session-close LLM extractions. Embeddings are generated asynchronously via waitUntil() — the first GET /context/:id?message=... after ingest may return message_relevance: null.

Performance SLA: < 50ms for non-session-close. Session-close adds ~2–5s for the LLM call.


3.2 Contact Management (AI Brain / Dashboard)

Create Contact

Creates a new contact profile. Idempotent — returns the existing contact (with created: false) if (tenant_id, external_id, channel) already exists.

POST /contact-intel/contacts
x-gateway-key: <key>
Content-Type: application/json
Field Type Required Description
external_id String Channel-specific ID (WhatsApp phone, Telegram chat ID, widget session)
channel Enum whatsapp | telegram | widget | instagram
display_name String Contact name

Response 201 (new) / 200 (existing):

{ "contact": { "id": "ct_arjun", "external_id": "...", "channel": "whatsapp" }, "created": true }

List Contacts

GET /contact-intel/contacts?sort=churn_risk&stage=fading&limit=20&offset=0
Param Type Default Description
sort Enum recent recent, churn_risk, messages, created
stage Enum all Filter: new, building, established, deep, fading, dormant, all
channel Enum all Filter: whatsapp, telegram, widget, instagram, all
limit Integer 20 Max results (1–100)
offset Integer 0 Pagination offset

Response 200:

{
  "contacts": [{ "id": "...", "displayName": "Arjun", "relationshipStage": "established", "churnRisk": 0.12, ... }],
  "total": 234,
  "limit": 20,
  "offset": 0
}

Get Contact Profile

GET /contact-intel/contacts/:id/profile

Response 200: { contact, state, memories (top 50 by importance), entities }


Delete Contact (GDPR)

Soft-deletes the contact row (scrubs PII), hard-deletes memories, legacy entities, and interaction logs. New CI tables (ci_entities, ci_emotional_snapshots, ci_relationship_snapshots, ci_contact_channels) cascade on contact FK delete.

DELETE /contact-intel/contacts/:id

Response 200: { "deleted": true }


Export Contact Data (GDPR)

GET /contact-intel/contacts/:id/export

Response 200:

{
  "export_date": "2026-06-29T00:00:00.000Z",
  "contact": { ... },
  "state": { ... },
  "memories": [ ... ],
  "entities": [ ... ],
  "interaction_count": 245
}

List Contact Memories

GET /contact-intel/contacts/:id/memories

Returns up to 100 memories (active + superseded) ordered by importance descending.

Response 200: { "memories": [{ id, memoryType, content, importance, accessCount, entityIds, createdAt, accessedAt }] }


Delete All Memories

DELETE /contact-intel/contacts/:id/memories

Hard-deletes all memories for the contact. Response 200: { "deleted": true }


Correct a Memory

Supersedes an incorrect memory with a correction. The new memory gets source: "contact_corrected" and confidence: 1.0. Also nullifies core_summary to force regeneration on next weekly consolidation.

POST /contact-intel/contacts/:id/correct
Content-Type: application/json
Field Type Required Description
hint String ≤500 Fragment of the incorrect memory (ILIKE search)
correction String ≤2000 The correct information to store

Response 200: { "corrected": true, "memory_id": "uuid" }

Response 404: { "error": { "code": "MEMORY_NOT_FOUND" } } — when no active memory matches the hint


3.3 Outreach Management

Schedule Outreach

POST /contact-intel/outreach
Content-Type: application/json

Request Body:

{
  "contact_id": "ct_arjun",
  "trigger_type": "scheduled",
  "scheduled_at": "2026-06-30T08:00:00Z",
  "message": "Hey! Hope the sprint is going well.",
  "context": { "event": "sprint_deadline" }
}

Response 201: { "id": "trg_789", "status": "pending", "scheduled_at": "..." }


List Outreach Triggers

GET /contact-intel/outreach?contact_id=ct_arjun&status=pending
Param Type Default Description
contact_id UUID Filter by contact
status Enum all pending, fired, failed, cancelled, expired, all
trigger_type Enum all scheduled, inactivity, milestone, recurring, all
limit Integer 20 Max results

Cancel Outreach Trigger

POST /contact-intel/outreach/:id/cancel

Response 200: { "cancelled": true }


Get Outreach Config

GET /contact-intel/outreach/config

Response 200:

{
  "enabled": true,
  "inactivityDays": 3,
  "maxInactivityAttempts": 2,
  "minGapDays": 3,
  "maxPerDay": 3,
  "quietHoursStart": 22,
  "quietHoursEnd": 8,
  "timezone": "Asia/Kolkata"
}

Update Outreach Config

PATCH /contact-intel/outreach/config
Content-Type: application/json

Send any subset of the config fields to update. Returns the updated config.


3.4 Internal Endpoints (Cron & Queue)

Scan Outreach Triggers (Cron — every 6h)

POST /internal/outreach/scan
x-gateway-key: <key>

Finds pending triggers whose scheduled_at has passed, generates messages, marks them fired. Also creates inactivity triggers for contacts exceeding inactivity_days.


Process Weekly Consolidation (Queue Consumer)

The Sunday 2am UTC cron enqueues one message per active contact to CONSOLIDATION_QUEUE. The queue consumer runs the 9-step consolidateContact() pipeline for each contact. See contact-memory-spec.md for the full pipeline.


4. Error Response Format

{
  "error": {
    "code": "CONTACT_NOT_FOUND",
    "message": "Contact ct_xyz does not exist for this tenant."
  }
}

Standard Error Codes:

Code HTTP Description
CONTACT_NOT_FOUND 404 Contact ID does not exist for this tenant
MEMORY_NOT_FOUND 404 No active memory matches the given hint
TRIGGER_NOT_FOUND 404 Outreach trigger ID does not exist
INVALID_INPUT 400 Malformed request body
PERMISSION_DENIED 403 User lacks required PBAC permission
QUIET_HOURS 409 Cannot schedule outreach during configured quiet hours
MAX_ATTEMPTS_REACHED 409 Inactivity outreach limit already reached for this contact
Contact Intelligence