Skip to main content

The big picture

This guide walks through a complete integration: your platform receives a collab pitch, pushes it into the Sight AI Outreach Agent via the REST API, and receives real-time webhook notifications as the agent triages, replies, and tracks the conversation.
Your platform ──POST──> Sight AI API ──> Outreach Agent ──reply──> Prospect
     ^                       │                │
     │                       │                │
     └──── webhook <─────────┴────────────────┘
     (reply_sent, status_changed, contact_suppressed)

Step 1: Register an OAuth client + authorize

  1. Open Integrations → Sight AI API → OAuth apps in the Sight AI app.
  2. Create a client with your app’s name and your HTTPS redirect URI (e.g. https://your-platform.com/oauth/callback). For a server-side platform, check Server-side app (issue a client secret) to register a confidential client and copy the client_id + client_secret (sai_ocsec_…) shown once. Otherwise copy just the client_id (public client).
  3. In your platform, add a “Connect Sight AI” button that redirects the user to Sight AI’s authorize endpoint with the scopes you need (webhooks:write, webhooks:read, inbox:write) + PKCE:
    GET https://app.trysight.ai/oauth/authorize
      ?response_type=code&client_id=YOUR_CLIENT_ID
      &redirect_uri=https://your-platform.com/oauth/callback
      &scope=webhooks:write webhooks:read inbox:write
      &code_challenge=PKCE_CHALLENGE&code_challenge_method=S256&state=RANDOM
    
  4. The user picks a site and approves the scopes on Sight AI’s consent screen. Your redirect handler exchanges the code for a sai_oauth_* access token at POST /oauth/token.
See Authentication for the OAuth flow and scope model, and Webhooks for the full sequence.

Step 2: Create the webhook subscription via API

After your platform has an access token, create the webhook subscription programmatically (no dashboard form needed):
curl -X POST https://app.trysight.ai/api/v1/sites/site_abc123/outreach/webhooks \
  -H "Authorization: Bearer sai_oauth_..." \
  -H "Content-Type: application/json" \
  -d '{
    "label": "CRM — inbound handler",
    "url": "https://your-platform.com/webhooks/sightai",
    "eventTypes": ["outreach.reply_sent", "outreach.conversation_status_changed", "outreach.contact_suppressed"]
  }'
The response includes the signing secret (shown once). Store it in your environment as SIGHTAI_WEBHOOK_SECRET. See Outreach Webhooks API for the full reference, and Webhooks for the event catalog.

Step 3: Push a message

When your platform receives a collab pitch (e.g. a prospect emailed your support inbox), POST it to the ingestion endpoint:
curl -s -X POST https://app.trysight.ai/api/v1/sites/site_abc123/outreach/inbox/messages \
  -H "Authorization: Bearer sai_oauth_..." \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: pitch-2026-06-29-editor-example-com-001" \
  -d '{
    "from": "editor@example.com",
    "fromName": "Jane Editor",
    "subject": "Collab request for your SEO post",
    "bodyText": "Hi, I run example.com and loved your piece on X. Would you consider adding a link to our guide on Y? Happy to reciprocate.",
    "runExtraction": true
  }'
The response includes the conversationId and opportunityId Sight AI created. Store these so you can correlate webhook events back to the original pitch.
{
  "ok": true,
  "siteId": "site_abc123",
  "conversationIds": ["conv_xyz"],
  "opportunityIds": ["opp_001"],
  "entrySource": "forwarded_actionable",
  "aiPaused": false
}
See Outreach Inbox for the full request/response reference.
Always send an Idempotency-Key header. If the request times out or fails, you can safely retry with the same key without creating a duplicate conversation.

Step 4: Verify the signature on incoming webhooks

Your webhook handler must verify the SightAI-Signature header before processing the payload. The header is in the format t=<unix>,v1=<hex> where the hex is HMAC-SHA256 over ${timestamp}.${raw_body} using your signing secret.
import crypto from 'node:crypto';

export async function POST(request) {
  const rawBody = await request.text();
  const sig = request.headers.get('SightAI-Signature');
  const eventType = request.headers.get('SightAI-Event');

  if (!verifySignature(rawBody, sig, process.env.SIGHTAI_WEBHOOK_SECRET)) {
    return new Response('Invalid signature', { status: 401 });
  }

  const event = JSON.parse(rawBody);
  // Process the event...

  return new Response('ok', { status: 200 });
}

function verifySignature(rawBody, signatureHeader, secret, toleranceSeconds = 300) {
  const parts = Object.fromEntries(
    signatureHeader.split(',').map((p) => p.split('=')),
  );
  const timestamp = Number(parts.t);
  const signature = parts.v1;
  if (!timestamp || !signature) return false;
  if (Math.floor(Date.now() / 1000) - timestamp > toleranceSeconds) return false;

  const expected = crypto
    .createHmac('sha256', secret)
    .update(`${timestamp}.${rawBody}`)
    .digest('hex');

  return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(signature));
}
See Webhooks → Verifying the signature for Python and more detail.

Step 5: Handle outreach.reply_sent

When the agent sends a reply, you’ll receive a outreach.reply_sent event:
{
  "id": "outreach.reply_sent:1719655200000:abc123",
  "type": "outreach.reply_sent",
  "siteId": "site_abc123",
  "teamId": "team_xyz",
  "timestamp": 1719655200000,
  "data": {
    "conversationId": "conv_xyz",
    "messageId": "msg_outbound_001",
    "mailgunMessageId": "<20260629...@mail-sightai.com>",
    "triage": "accept",
    "autoResolved": false,
    "sendOutcome": "sent"
  }
}
Use the conversationId to correlate this reply with the original pitch you pushed in Step 3. Update your CRM with the reply details (e.g. “Sight AI agent replied — accepted the collab request”).

Step 6: Handle outreach.conversation_status_changed

When the conversation reaches a terminal state, you’ll receive this event:
{
  "type": "outreach.conversation_status_changed",
  "data": {
    "conversationId": "conv_xyz",
    "oldStatus": "open",
    "newStatus": "resolved",
    "resolvedBy": "agent",
    "resolutionOutcome": "accept"
  }
}
Map this to your CRM’s deal stages:
  • resolved → “Closed — collab accepted”
  • declined → “Closed — declined”
  • awaiting_human → “Needs human review”

Step 7: Handle outreach.contact_suppressed

When a prospect opts out (or bounces / complains), you’ll receive this event. Remove them from your outbound lists immediately — continuing to email a suppressed contact risks deliverability damage.
{
  "type": "outreach.contact_suppressed",
  "data": {
    "email": "prospect@example.com",
    "reason": "opted_out",
    "source": "inbound_classifier",
    "upgraded": false
  }
}

Troubleshooting

ProblemCauseFix
402 responseTeam subscription or site license problemEnsure the team has an active subscription and the site is licensed
403 INSUFFICIENT_SCOPEAPI key lacks inbox:writeRe-create the key with the inbox:write scope
429 responseRate limit or per-site daily ingestion capSlow down your request rate; contact support to raise the daily cap
Webhooks not arrivingSubscription auto-disabled or endpoint returning non-2xxCheck the Developers page for auto-disabled status; verify your endpoint returns 200 within 10s
Duplicate conversationsMissing Idempotency-Key header on retriesAlways send the same Idempotency-Key when retrying a failed request
aiPaused: true in responseAgent is inactive, auto-reply is off, or sender is a noreply addressTurn on the Outreach Agent in Inbox → Settings; the message still lands in the inbox for manual triage

Next steps