API reference

Pi DeepSearch

Natural language transaction search and analytics API.

Base URL

https://api.payintelli.com/v1/deepsearch

Endpoints

35 documented routes

Authentication

All REST endpoints (except /health) require a Cognito JWT in the Authorization header:

Authorization: Bearer <access_token>

All user-facing endpoints use the CLIENT_HUB pool (CH_API_DEEPSEARCH): conversations, config, onboarding, feedback, benchmarking, and schema visualization.

Tokens expire — always handle 401 by refreshing the Cognito token.

Common Error Responses

StatusMeaning
400 Bad RequestValidation failure — see detail field
401 UnauthorizedMissing, expired, or invalid token
403 ForbiddenToken valid but insufficient permissions
404 Not FoundResource does not exist
409 ConflictDuplicate resource or state conflict
429 Too Many RequestsRate limit exceeded
500 Internal Server ErrorUnexpected server-side error — retry with backoff

Error body:

Endpoint Groups

GroupPrefixAuth pool
WebSocket Chatwss://…/api/v1/chatCLIENT_HUB
Conversations/api/v1/conversationsCLIENT_HUB
Feedback (user)/api/v1/conversations/{id}/feedbackCLIENT_HUB
Config — Datasources/api/v1/deepsearch/configCLIENT_HUB
Config — KPIs/api/v1/deepsearch/config/datasource/{platform}/kpiCLIENT_HUB
Config — Budgets/api/v1/deepsearch/config/budgetCLIENT_HUB
Onboarding/api/v1/deepsearch/onboardingCLIENT_HUB
Schema Visualization/schema/flow, /schema/analyzeCLIENT_HUB
Benchmarking/api/v1/deepsearch/benchmarkCLIENT_HUB
Monitoring/healthNone

1. WebSocket Chat

Endpoint: wss://ds-api-dev.payintelli.com/api/v1/chat

All messages in both directions are JSON.

Connection

Connect with the Cognito JWT as a query parameter or via the Sec-WebSocket-Protocol header:

// Option A — query param (simplest)
const socket = new WebSocket(
  `wss://ds-api-dev.payintelli.com/api/v1/chat?token=${accessToken}`
);

// Option B — subprotocol header (to resume an existing conversation)
const socket = new WebSocket(
  `wss://ds-api-dev.payintelli.com/api/v1/chat?conversation_id=${conversationId}`,
  [accessToken]
);

Sending a Message

socket.send(JSON.stringify({
  type: "message",
  message: "What is total revenue this month?",
  conversation_id: currentConversationId, // omit on the first message of a new conversation
}));

Event Flow

A normal conversation turn:

When the AI runs a database query (tool use):

When the AI produces a chart:

Server Events Reference

title in stream_end is only present on the first message of a new conversation. Use it to set the conversation name in the UI without a separate API call.

Chart Data Event

The chart_data event contains a ready-to-use Apache ECharts option object. The server sends data and structure only — no colours, fonts, or theme settings — so you can apply your own theme:

Pass echarts.option directly to myChart.setOption(option).

Adaptive Card Event

Used for structured tables, insights, alerts, and email drafts:

card_type values: result (data table), insight (text conclusion), alert (anomaly), email (draft).

Error Codes

All errors arrive as { type: "error", code: "...", message: "..." }.

Important: AI_TIMEOUT does not close the WebSocket connection. Do not reconnect — just let the user resend. Reconnecting creates a new session and loses conversation context.

Connection Lifecycle

On connect — store the conversation ID:

socket.addEventListener("message", (event) => {
  const data = JSON.parse(event.data);
  if (data.type === "connected") {
    currentConversationId = data.conversation_id;
    if (data.is_new) addConversationToSidebar(data.conversation_id);
  }
});

On unexpected disconnect — reconnect with the same conversation ID:

socket.addEventListener("close", () => {
  setTimeout(() => reconnect(currentConversationId), 2000);
});

Conversation Cap

Each user is limited to 20 conversations. When the 21st is started, the oldest is automatically deleted server-side. Refresh the conversation list after a new conversation is created to reflect the deletion.

Content Moderation

If a message is blocked, a moderated event fires instead of stream_start:

Show the message text inline and re-enable the input. The session remains open.


Restoring Visuals from Conversation History

GET

1. WebSocket Chat

Description

For adaptive cards, chart_data.type is "adaptive_card" and card fields are spread at the top level. Check message.has_chart before rendering.

Endpoint Path

/api/v1/conversations/{id}/history

2. Conversations

All endpoints require CLIENT_HUB auth. Conversations are scoped to the authenticated user — cross-user access returns 403.

Conversations

GET

2. Conversations

Description

List all conversations for the authenticated user, sorted by most recently updated.

Response 200


Endpoint Path

/api/v1/conversations

Response Body

{
  "conversations": [
    {
      "conversation_id": "01930f4a-...",
      "metadata": {
        "user_id": "alice@example.com",
        "client_id": "100002",
        "chat_title": "Revenue analysis",
        "platform": "on_prem_mysql",
        "last_updated": 1713174600,
        "is_favourite": false
      }
    }
  ],
  "count": 3,
  "user_id": "alice@example.com",
  "client_id": "100002"
}

Conversations History

GET

2. Conversations

Description

Return the full message history for a conversation, with feedback embedded in each assistant message.

Path parameters

Response 200

Messages without feedback have no feedback key. Check has_chart before rendering a visual.


Endpoint Path

/api/v1/conversations/{conversation_id}/history

Request Parameters

ParameterTypeRequiredDescription
`conversation_id`stringOptionalUUID of the conversation

Response Body

{
  "conversation_id": "01930f4a-...",
  "metadata": {
    "user_id": "alice@example.com",
    "chat_title": "Revenue analysis",
    "platform": "on_prem_mysql",
    "last_updated": 1713174600
  },
  "messages": [
    {
      "role": "user",
      "content": "What was total revenue last month?"
    },
    {
      "role": "assistant",
      "content": "Total revenue last month was $1,234,567.",
      "has_chart": true,
      "chart_data": {
        "type": "chart_data",
        "chart_data": { "chart_type": "bar", "echarts": { "option": {} } }
      },
      "has_feedback": true,
      "feedback": {
        "reaction": "thumbs_up",
        "feedback_text": null,
        "submitted_at": "2026-04-15T10:30:00.000000+00:00",
        "message_index": 1,
        "submitted_by": "alice@example.com",
        "client_id": "100002"
      }
    }
  ]
}

Conversations Rename Title

PATCH

2. Conversations

Description

Rename a conversation.

Path parameters

Query parameters

Response 200


Endpoint Path

/api/v1/conversations/{conversation_id}/rename/title

Request Parameters

ParameterTypeRequiredDescription
`conversation_id`stringOptionalUUID of the conversation
`new_title`stringRequiredNew title, 1–200 characters

Response Body

{
  "conversation_id": "01930f4a-...",
  "conversation_title": "Monthly Revenue Deep Dive",
  "client_id": "100002",
  "status": "success"
}

Conversations Delete

DELETE

2. Conversations

Description

Permanently delete a conversation and all its messages.

Path parameters

Response 200


Endpoint Path

/api/v1/conversations/{conversation_id}

Request Parameters

ParameterTypeRequiredDescription
`conversation_id`stringOptionalUUID of the conversation

Response Body

{
  "success": true,
  "conversation_id": "01930f4a-...",
  "client_id": "100002",
  "message": "Conversation deleted successfully"
}

Conversations Favourite

POST

2. Conversations

Description

Mark a conversation as a favourite.

Query parameters

Response 200


Endpoint Path

/api/v1/conversations/{conversation_id}/favourite

Request Parameters

ParameterTypeRequiredDescription
`is_favourite`booleanRequired`true` to mark, `false` to unmark

Response Body

{
  "conversation_id": "01930f4a-...",
  "is_favourite": true,
  "status": "success"
}

Conversations Unfavourite

POST

2. Conversations

Description

Remove the favourite mark from a conversation.

Response 200


Endpoint Path

/api/v1/conversations/{conversation_id}/unfavourite

Response Body

{
  "conversation_id": "01930f4a-...",
  "is_favourite": false,
  "status": "success"
}

3. Feedback (User-Facing)

Conversations Feedback

POST

3. Feedback (User-Facing)

Description

Submit a thumbs-up or thumbs-down rating for a specific assistant message.

Path parameters

Request body

message_index must point to an assistant message. Indexing into a user message returns 400.

Response 200

Response fields

Errors

Feedback can only be submitted once per message. There is no update or delete endpoint.


Endpoint Path

/api/v1/conversations/{conversation_id}/feedback

Request Body

{
  "feedback_reaction": "thumbs_up",
  "feedback_text": "Very accurate.",
  "message_index": 1
}

Request Parameters

ParameterTypeRequiredDescription
`conversation_id`stringOptionalThe conversation containing the message to rate
`feedback_reaction`stringRequired`"thumbs_up"` or `"thumbs_down"`
`feedback_text`stringOptionalOptional comment, max 1000 chars. Send `null` or omit to leave empty
`message_index`integerRequiredZero-based index of the **assistant** message in the conversation
`feedback_id`stringOptionalUnique ID — format: `{conversation_id}#{message_index}#{uuid}`
`feedback.reaction`stringOptional`"thumbs_up"` or `"thumbs_down"`
`feedback.feedback_text`string \Optionalnull
`feedback.submitted_at`stringOptionalISO 8601 UTC timestamp
`feedback.submitted_by`stringOptionalEmail of the submitting user

Response Body

{
  "success": true,
  "message": "Feedback submitted successfully",
  "conversation_id": "01930f4a-...",
  "feedback_id": "01930f4a-...#1#0698639b-...",
  "feedback": {
    "reaction": "thumbs_up",
    "feedback_text": "Very accurate.",
    "submitted_at": "2026-04-15T10:30:00.000000+00:00",
    "message_index": 1,
    "submitted_by": "alice@example.com",
    "client_id": "100002"
  }
}

4. Config — Datasources

Manage per-client datasource configurations. Requires CLIENT_HUB auth.

Deepsearch Config

GET

4. Config — Datasources

Description

Return the full configuration for the authenticated client, including all platforms, KPIs, budgets, and governance settings.

Response 200


Endpoint Path

/api/v1/deepsearch/config

Response Body

{
  "platforms": [
    {
      "platformKey": "on_prem_mysql",
      "selectedTables": ["transactions", "merchants"],
      "columns": {
        "transactions": ["id", "amount", "status", "created_at"],
        "merchants": ["id", "name", "mcc_code"]
      },
      "kpis": [
        {
          "name": "Approval Rate",
          "formula": "approved / total",
          "query": "SELECT COUNT(*) FILTER (WHERE status='approved') / COUNT(*)::float FROM transactions"
        }
      ],
      "piiClassification": {
        "transactions.card_number": { "contains_PII": true, "PII_Type": "Credit Card Number" }
      },
      "ssasMetadata": null
    }
  ],
  "budgets": [
    {
      "id": "uuid",
      "name": "Monthly Cap",
      "amount": 50000.00,
      "currency": "USD",
      "period": "monthly"
    }
  ],
  "governance": {},
  "updatedAt": "2026-04-15T10:30:00.000000+00:00"
}

Deepsearch Config Datasource

GET

4. Config — Datasources

Description

Return the config for a single platform.

Path parameters

Response 200 — same shape as a single item in the platforms array above, plus a top-level updatedAt field.

Errors: 404 if the platform is not configured for this client.


Endpoint Path

/api/v1/deepsearch/config/datasource/{platform}

Request Parameters

ParameterTypeRequiredDescription
`platform`stringOptionalPlatform key e.g. `on_prem_mysql`, `snowflake`, `azure_sql`

Deepsearch Config Datasource Delete

DELETE

4. Config — Datasources

Description

Remove a datasource and all its metadata (tables, columns, KPIs, PII classifications) from the client's config.

Response 200


Endpoint Path

/api/v1/deepsearch/config/datasource/{platform}

Response Body

{ "success": true, "message": "Datasource 'on_prem_mysql' removed" }

Deepsearch Config Datasource Update

PATCH

4. Config — Datasources

Description

Update connection credentials for an existing datasource. Credentials are KMS-encrypted at rest.

Request body — platform-specific credential fields, e.g.:

Response 200


Endpoint Path

/api/v1/deepsearch/config/datasource/{platform}

Request Body

{
  "host": "db.internal",
  "port": 3306,
  "username": "deepsearch_user",
  "password": "new-password"
}

Response Body

{ "success": true, "message": "Datasource 'on_prem_mysql' updated" }

Deepsearch Config Datasource Schema

PATCH

4. Config — Datasources

Description

Add or remove tables and columns from an already-connected platform without resetting credentials.

Request body

All fields are optional — omit what you don't need.

Response 200

Errors: 400 if addColumns references a table not in selectedTables.


Endpoint Path

/api/v1/deepsearch/config/datasource/{platform}/schema

Request Body

{
  "addTables": ["refunds"],
  "removeTables": ["legacy_log"],
  "addColumns": {
    "refunds": ["id", "amount", "reason"]
  },
  "removeColumns": {
    "transactions": ["internal_flag"]
  }
}

Request Parameters

ParameterTypeRequiredDescription
`addTables`string[]OptionalTables to add to `selectedTables`
`removeTables`string[]OptionalTables to remove (also drops their column entries)
`addColumns`objectOptional`{ "table": ["col1", "col2"] }` — table must already be in `selectedTables`
`removeColumns`objectOptional`{ "table": ["col_to_drop"] }`

Response Body

{
  "success": true,
  "platform": "on_prem_mysql",
  "schema": {
    "selectedTables": ["transactions", "merchants", "refunds"],
    "columns": {
      "transactions": ["id", "amount", "status"],
      "merchants": ["id", "name"],
      "refunds": ["id", "amount", "reason"]
    }
  }
}

5. Config — KPIs

KPIs are stored per-platform under the client's config. Requires CLIENT_HUB auth.

Deepsearch Config Datasource Kpi

POST

5. Config — KPIs

Description

Add a new KPI to a platform.

Path parameters

Request body

Response 200

Errors: 404 if the platform is not found. 409 if a KPI with the same name already exists.


Endpoint Path

/api/v1/deepsearch/config/datasource/{platform}/kpi

Request Body

{
  "name": "Decline Rate",
  "formula": "declined / total",
  "query": "SELECT COUNT(*) FILTER (WHERE status='declined') / COUNT(*)::float FROM transactions",
  "description": "Fraction of transactions declined"
}

Request Parameters

ParameterTypeRequiredDescription
`platform`stringOptionalPlatform key

Response Body

{
  "success": true,
  "message": "KPI 'Decline Rate' added",
  "kpi": {
    "name": "Decline Rate",
    "formula": "declined / total",
    "query": "SELECT ...",
    "description": "Fraction of transactions declined"
  }
}

Deepsearch Config Datasource Kpi Update

PATCH

5. Config — KPIs

Description

Update an existing KPI by name.

Path parameters

Request body — any subset of KPI fields to overwrite:

Response 200


Endpoint Path

/api/v1/deepsearch/config/datasource/{platform}/kpi/{kpi_name}

Request Body

{ "query": "SELECT ...", "description": "Updated description" }

Request Parameters

ParameterTypeRequiredDescription
`platform`stringOptionalPlatform key
`kpi_name`stringOptionalExact KPI name (URL-encode spaces)

Response Body

{
  "success": true,
  "message": "KPI 'Decline Rate' updated",
  "kpi": { "name": "Decline Rate", "query": "SELECT ...", "description": "Updated description" }
}

Deepsearch Config Datasource Kpi Delete

DELETE

5. Config — KPIs

Description

Delete a KPI by name.

Response 200


Endpoint Path

/api/v1/deepsearch/config/datasource/{platform}/kpi/{kpi_name}

Response Body

{ "success": true, "message": "KPI 'Decline Rate' deleted" }

6. Config — Budgets

Budget entries are stored at the client level, not per-platform. Requires CLIENT_HUB auth.

Deepsearch Config Budget

POST

6. Config — Budgets

Description

Add a budget entry.

Request body

Response 200


Endpoint Path

/api/v1/deepsearch/config/budget

Request Body

{
  "name": "Monthly Spend Cap",
  "amount": 50000.00,
  "currency": "USD",
  "period": "monthly"
}

Request Parameters

ParameterTypeRequiredDescription
`name`stringRequiredDisplay name
`amount`floatRequiredBudget amount
`currency`stringOptionalDefault `"USD"`
`period`stringRequirede.g. `"monthly"`, `"quarterly"`, `"annual"`

Response Body

{
  "success": true,
  "message": "Budget added",
  "budget": { "id": "uuid", "name": "Monthly Spend Cap", "amount": 50000.0, "currency": "USD", "period": "monthly" }
}

Deepsearch Config Budget Update

PATCH

6. Config — Budgets

Description

Update an existing budget. This is a full replacement, not a merge.

Path parameters

Request body — same shape as the create request.

Response 200


Endpoint Path

/api/v1/deepsearch/config/budget/{budget_id}

Request Parameters

ParameterTypeRequiredDescription
`budget_id`stringOptionalUUID of the budget to update

Response Body

{
  "success": true,
  "message": "Budget updated",
  "budget": { "id": "uuid", "name": "Monthly Spend Cap", "amount": 60000.0, "currency": "USD", "period": "monthly" }
}

Deepsearch Config Budget Delete

DELETE

6. Config — Budgets

Description

Delete a budget entry.

Response 200


Endpoint Path

/api/v1/deepsearch/config/budget/{budget_id}

Response Body

{ "success": true, "message": "Budget deleted" }

7. Onboarding

The onboarding flow is sequential: environment → test-connection → list-databases → list-tables → analyze-pii → analyze-kpis → complete. Requires CLIENT_HUB auth.

Deepsearch Onboarding Environment

POST

7. Onboarding

Description

Save business environment metadata (step 1).

Request body

Response 200


Endpoint Path

/api/v1/deepsearch/onboarding/environment

Request Body

{
  "environmentName": "Production",
  "businessEntity": "Acme Corp",
  "environmentType": "production",
  "timeZone": "America/New_York",
  "description": "Main production database"
}

Response Body

{
  "success": true,
  "message": "Environment information saved successfully",
  "data": {
    "environmentName": "Production",
    "businessEntity": "Acme Corp",
    "environmentType": "production",
    "timeZone": "America/New_York",
    "description": "Main production database"
  }
}

Deepsearch Onboarding Test Connection

POST

7. Onboarding

Description

Validate database credentials for a given platform. On success, credentials are KMS-encrypted and stored (step 2).

Request body — fields used depend on platform:

Supported platforms and required fields:

Response 200

Errors: 400 with a descriptive message if the connection cannot be established or required fields are missing.


Endpoint Path

/api/v1/deepsearch/onboarding/test-connection

Request Body

{
  "platform": "on_prem_mysql",
  "host": "db.internal",
  "port": 3306,
  "username": "deepsearch",
  "password": "secret"
}

Response Body

{
  "success": true,
  "message": "Connection successful",
  "data": { "connectionValidated": true, "testedAt": "2026-04-15T10:30:00Z" }
}

Deepsearch Onboarding Schema

GET

7. Onboarding

Description

Retrieve the table listing for the currently configured platform. Currently supported for Azure SQL and BigQuery only.

Response 200


Endpoint Path

/api/v1/deepsearch/onboarding/schema

Response Body

{
  "success": true,
  "message": "Schema details loaded successfully",
  "data": {
    "platform": "azure_sql",
    "schemas": [
      {
        "schema": "PaymentsDB",
        "tables": [
          {
            "tableName": "transactions",
            "rowCount": 5000000,
            "columnCount": 12,
            "primaryKeys": ["id"],
            "foreignKeys": [],
            "hasPII": false
          }
        ]
      }
    ]
  }
}

Deepsearch Onboarding List Databases

POST

7. Onboarding

Description

List available databases/datasets for a connected platform (step 3).

Request body

Response 200


Endpoint Path

/api/v1/deepsearch/onboarding/list-databases

Request Body

{ "platform": "on_prem_mysql" }

Response Body

{
  "success": true,
  "message": "Found 3 dataset(s)",
  "data": { "databases": ["payments", "analytics", "reporting"] }
}

Deepsearch Onboarding List Tables

POST

7. Onboarding

Description

List tables within a specific database (step 4).

Request body

Response 200


Endpoint Path

/api/v1/deepsearch/onboarding/list-tables

Request Body

{
  "platform": "on_prem_mysql",
  "database": "payments",
  "snowflakeSchema": null
}

Request Parameters

ParameterTypeRequiredDescription
`platform`stringRequiredPlatform key
`database`stringRequiredDatabase/dataset name returned by `list-databases`
`snowflakeSchema`stringOptionalSnowflake schema name (Snowflake only)

Response Body

{
  "success": true,
  "message": "Found 8 table(s)",
  "data": {
    "database": "payments",
    "tables": [
      {
        "table": "transactions",
        "fullName": "payments.transactions",
        "schema_name": "payments",
        "rowCount": 5000000,
        "primaryKey": ["id"],
        "columns": [
          { "name": "id", "type": "bigint", "isPrimaryKey": true, "isNullable": false },
          { "name": "amount", "type": "decimal", "isPrimaryKey": false, "isNullable": false }
        ]
      }
    ]
  }
}

Deepsearch Onboarding Analyze Pii

POST

7. Onboarding

Description

Analyze selected tables for PII columns using Claude AI. Returns a Server-Sent Events (SSE) stream (step 5).

Request body

SSE event stream

Consume with EventSource or fetch + ReadableStream. Each event follows the SSE format:

event: <event_type>
data: <json_payload>

Each column in a table_complete event is enriched with PII analysis fields:


Endpoint Path

/api/v1/deepsearch/onboarding/analyze-pii

Request Body

{
  "name": "card_number",
  "type": "varchar",
  "contains_PII": true,
  "Confidence_Score": 0.98,
  "PII_Type": "Credit Card Number",
  "Reasoning": "Column name 'card_number' strongly indicates payment card data."
}

Deepsearch Onboarding Analyze Kpis

POST

7. Onboarding

Description

Generate KPI suggestions for a set of tables using Claude AI (step 6).

Request body

Supported platform values: on_prem_mysql, on_prem_postgres, snowflake, on_prem_sql_server, bigquery, azure_sql, aws_aurora_postgres, fabric

Response 200


Endpoint Path

/api/v1/deepsearch/onboarding/analyze-kpis

Request Body

{
  "platform": "on_prem_mysql",
  "tableDetails": [
    {
      "tableName": "transactions",
      "columns": ["id", "amount", "status", "created_at"]
    }
  ]
}

Response Body

{
  "domains": [
    {
      "domain": "Transaction Performance",
      "kpis": [
        {
          "name": "Approval Rate",
          "description": "Percentage of transactions approved",
          "formula": "approved_count / total_count",
          "query": "SELECT COUNT(*) FILTER (WHERE status='approved') / COUNT(*)::float AS approval_rate FROM transactions"
        }
      ]
    }
  ]
}

Deepsearch Onboarding Kpi Edit

POST

7. Onboarding

Description

Edit or create a KPI based on natural language instructions. Returns an SSE stream.

Request body

Set original_kpi to null to create a brand-new KPI from scratch.

SSE events


Endpoint Path

/api/v1/deepsearch/onboarding/kpi/edit

Request Body

{
  "original_kpi": {
    "name": "Approval Rate",
    "formula": "approved / total",
    "query": "SELECT ..."
  },
  "instruction": {
    "query": "Break this down by merchant category",
    "selected_columns": ["transactions.mcc_code", "transactions.status", "transactions.id"]
  }
}

Deepsearch Onboarding Complete

POST

7. Onboarding

Description

Mark onboarding as complete for a platform. Saves the final schema, KPIs, PII classifications, budgets, and governance config (step 7).

Request body

Response 200


Endpoint Path

/api/v1/deepsearch/onboarding/complete

Request Body

{
  "platformKey": "on_prem_mysql",
  "selectedTables": ["transactions", "merchants"],
  "selectedColumns": {
    "transactions": ["id", "amount", "status", "created_at"],
    "merchants": ["id", "name", "mcc_code"]
  },
  "piiClassification": {
    "transactions.pan": { "contains_PII": true, "PII_Type": "Credit Card Number" }
  },
  "kpis": [
    {
      "name": "Approval Rate",
      "formula": "approved / total",
      "query": "SELECT COUNT(*) FILTER (WHERE status='approved') / COUNT(*)::float FROM transactions"
    }
  ],
  "budgets": [],
  "governance": {},
  "ssasMetadata": null
}

Response Body

{
  "success": true,
  "platformKey": "on_prem_mysql",
  "updatedAt": "2026-04-15T10:30:00.000000+00:00"
}

8. Schema Visualization

Endpoints for React Flow schema diagrams. Requires CLIENT_HUB auth.

Schema Flow

GET

8. Schema Visualization

Description

Return a static demo React Flow graph (hardcoded sample schema for UI prototyping).

Response 200


Endpoint Path

/schema/flow

Response Body

{
  "nodes": [
    {
      "id": "users",
      "type": "tableNode",
      "position": { "x": 50, "y": 200 },
      "data": {
        "label": "users",
        "columns": [
          { "name": "id", "type": "UUID", "pk": true, "fk": null, "nullable": false },
          { "name": "email", "type": "VARCHAR(255)", "pk": false, "fk": null, "nullable": false }
        ]
      }
    }
  ],
  "edges": [
    {
      "id": "e1",
      "source": "orders",
      "target": "users",
      "sourceHandle": "user_id",
      "targetHandle": "id",
      "type": "smoothstep",
      "animated": false,
      "label": "user_id → users.id",
      "markerEnd": { "type": "ArrowClosed" }
    }
  ]
}

Schema Analyze

POST

8. Schema Visualization

Description

Use Claude AI to infer relationships between a set of user-provided tables and return a React Flow graph.

Request body

Response 200

Errors: 400 if no tables are provided. 502 if Claude returns invalid JSON.


Endpoint Path

/schema/analyze

Request Body

{
  "selectedTables": [
    {
      "table": "transactions",
      "fullName": "payments.transactions",
      "db_schema": "payments",
      "primaryKey": ["id"],
      "columns": [
        { "name": "id", "type": "bigint" },
        { "name": "merchant_id", "type": "bigint" }
      ]
    },
    {
      "table": "merchants",
      "fullName": "payments.merchants",
      "db_schema": "payments",
      "primaryKey": ["id"],
      "columns": [
        { "name": "id", "type": "bigint" },
        { "name": "name", "type": "varchar" }
      ]
    }
  ]
}

Response Body

{
  "flow": {
    "nodes": [ { "id": "transactions", "type": "tableNode", "position": { "x": 60, "y": 60 }, "data": { "label": "transactions", "columns": [] } } ],
    "edges": [ { "id": "e0", "source": "transactions", "target": "merchants", "sourceHandle": "merchant_id", "targetHandle": "id", "type": "smoothstep", "label": "many-to-one" } ]
  },
  "relationships": [
    {
      "from_table": "transactions",
      "from_column": "merchant_id",
      "to_table": "merchants",
      "to_column": "id",
      "type": "many-to-one"
    }
  ],
  "views": []
}

9. Benchmarking

All benchmarking endpoints require CLIENT_HUB auth.

Deepsearch Benchmark Metrics

GET

9. Benchmarking

Description

Return all active benchmark metrics. Use metric_key as the parameter value in the other benchmark endpoints.

No query parameters.

Response 200


Endpoint Path

/api/v1/deepsearch/benchmark/metrics

Response Body

{
  "metrics": [
    {
      "metric_key": "approval_rate",
      "display_name": "Approval Rate",
      "description": "Fraction of transactions that were approved",
      "unit": "ratio"
    },
    {
      "metric_key": "variance_to_average",
      "display_name": "Variance to Average",
      "description": "Statistical variance of daily approval-rate outcomes from their within-day mean",
      "unit": "ratio"
    }
  ]
}

Deepsearch Benchmark Industry Position

GET

9. Benchmarking

Description

Return the client's own metric value for the most recent (or a specific) snapshot date, alongside anonymised industry percentile bands.

Query parameters

Response 200 — opted in, data available

percentile_position values

client_value and industry_bands are ratios (0–1). Multiply by 100 to display as percentages where unit = "ratio".

Response 200 — not opted in

Response 200 — opted in but cohort too small

Error — unknown metric


Endpoint Path

/api/v1/deepsearch/benchmark/industry-position

Request Parameters

ParameterTypeRequiredDescription
`metric_key`stringRequired`approval_rate` or `variance_to_average`
`date`stringOptionalISO date `YYYY-MM-DD`. Omit to use the most recent available snapshot

Response Body

{ "detail": "Unknown metric_key 'xyz'. Available: approval_rate, variance_to_average." }

Deepsearch Benchmark Opt In

POST

9. Benchmarking

Description

Opt the client in (or out) of benchmarking and set their industry profile. Safe to call repeatedly — it upserts.

Request body

Response 200

Error — invalid industry_category

Benchmarking rate limits


Endpoint Path

/api/v1/deepsearch/benchmark/opt-in

Request Body

{
  "mcc_code": "6012",
  "industry_category": "FINTECH",
  "opted_into_benchmark": true
}

Request Parameters

ParameterTypeRequiredDescription
`mcc_code`stringOptional4-digit Merchant Category Code
`industry_category`stringOptional`FINTECH`, `ECOMMERCE`, `RETAIL`, `TRAVEL`, `HEALTHCARE`, `EDUCATION`, `OTHER`
`opted_into_benchmark`booleanOptional`true` to opt in, `false` to opt out

Response Body

{ "detail": "Invalid industry_category 'GAMING'. Must be one of: FINTECH, ECOMMERCE, ..." }

10. Monitoring

Health

GET

10. Monitoring

Description

Returns system health status. No authentication required. Used by load balancer health checks.

Response 200

status is "healthy" when all four dependencies report connected/reachable; otherwise "degraded".

Dependency fields


Endpoint Path

/health

Request Parameters

ParameterTypeRequiredDescription
`redis`stringOptional
`postgres`stringOptional
`dynamodb`stringOptional
`anthropic`stringOptional

Response Body

{
  "status": "healthy",
  "version": "1.4.2",
  "model": "claude-sonnet-4-6",
  "active_ws": 3,
  "connections": [
    {
      "user_email": "alice@example.com",
      "client_id": "100002",
      "platform": "on_prem_mysql",
      "conversation_id": "01930f4a-...",
      "idle_seconds": 12,
      "duration_seconds": 145
    }
  ],
  "redis":     { "status": "connected", "latency_ms": 1 },
  "postgres":  { "status": "connected", "latency_ms": 4 },
  "dynamodb":  { "status": "connected", "latency_ms": 18 },
  "anthropic": { "status": "reachable", "latency_ms": 42 }
}

Rate Limits

All routes are protected by Redis-backed rate limiting. Exceeded limits return HTTP 429:

The WebSocket endpoint returns { "type": "error", "code": "RATE_LIMITED", "message": "..." } instead of an HTTP response.

Benchmarking-specific limits:

RouteLimit
GET /api/v1/deepsearch/benchmark/trends20 requests / 60 s per user
All other /benchmark/ routes60 requests / 60 s per user