Connic
Connectors

Webhooks

HTTP webhooks let external systems trigger your agents via HTTP requests. They support JSON, form data, and file uploads.

Last updated
Inbound Mode
Fire-and-forget triggers for async agent runs. The webhook returns immediately with run IDs while agents process in the background.

Setup Instructions

1

Create the connector

Open your agent, click Add inbound connector in the Connector Flow, then Create New Connector and select HTTP Webhook.

2

Select Inbound (Fire & Forget) and copy credentials

Choose the mode and click Create. The detail drawer opens automatically with the Webhook URL and Secret in the Configuration section. To view them later, hover over the connector in the Connector Flow and click the Eye icon.

3

Send requests

Send requests from your application to the URL, passing the secret in the X-Connic-Secret header unless you disabled connector authentication because middleware authenticates first.

How Inbound Works

When an inbound webhook receives a request, it queues the agent run and returns a response with the run IDs. The agent processes in the background. Best for background processing, high-volume ingestion, and event-driven architectures.

Response Format

response.json
{
  "status": "ok",
  "dispatched_to": 2,
  "run_ids": [
    "550e8400-e29b-41d4-a716-446655440000",
    "550e8400-e29b-41d4-a716-446655440001"
  ]
}

Sending Requests

Webhooks accept both GET and POST requests with various content types.

JSON Requests

The most common way to trigger webhooks. Send a POST request with a JSON body (application/json). The entire JSON payload is passed to your agent as input.

terminal
curl -X POST <webhook-url> \
  -H "Content-Type: application/json" \
  -H "X-Connic-Secret: <your-secret-key>" \
  -d '{
    "prompt": "Analyze this data",
    "data": {"key": "value"}
  }'

Form Data Requests

Useful for simple integrations or when your source system sends form data (application/x-www-form-urlencoded). Each form field becomes a key-value pair in the payload.

terminal
curl -X POST <webhook-url> \
  -H "X-Connic-Secret: <your-secret-key>" \
  -F "message=Process this invoice" \
  -F "customer_id=12345"

GET Requests

For simple triggers, use GET requests with query parameters. All query parameters (except secret) are passed to the agent.

terminal
curl "<webhook-url>?secret=<your-secret-key>&prompt=hello"

Get Your Webhook URL & Secret

The webhook URL includes your webhook_id, and theX-Connic-Secret is generated when you create the connector. Authentication is enabled by default. Disable Require Authentication only when your middleware validates requests before they reach Connic.

  1. Open your agent's detail page in the Agents section
  2. Add a webhook connector via the + button on the connector flow, or select an existing one
  3. Click the eye icon on the connector to open its details
  4. Copy the Webhook URL and Secret from the connector details

File Uploads

Send files to your agents using multipart/form-data. Files are passed directly to the LLM for analysis.

Files are extracted and sent to the model as inline data:

terminal
curl -X POST <webhook-url> \
  -H "X-Connic-Secret: <your-secret-key>" \
  -F "instructions=What is the total of this invoice?" \
  -F "file=@/path/to/invoice.pdf"

Upload multiple files in a single request:

terminal
curl -X POST <webhook-url> \
  -H "X-Connic-Secret: <your-secret-key>" \
  -F "message=Compare these two documents" \
  -F "file1=@document1.pdf" \
  -F "file2=@document2.pdf"

Multipart requests land in context["payload"] as a normalised dict with text parts as top-level keys and uploads under files (each file's bytes are base64-encoded). The LLM-facing content is reconstructed automatically — each file becomes a binary part, and the leading text part is the payload with only files removed:

payload shape
# context["payload"] inside middleware before()
{
  "customer_id": "12345",      # multipart text fields sit at the top level
  "tag": ["a", "b"],           # repeated keys are grouped into a list
  "message": "Process this invoice",
  "files": [
    {
      "name": "invoice.pdf",     # original filename
      "field_name": "file",      # form field name
      "mime_type": "application/pdf",
      "data": "<base64-bytes>",  # always base64-encoded ASCII
      "size": 12345              # bytes
    }
  ]
}

# If no supported file made it through, payload collapses to just the
# top-level form fields — no "files" key.

Unsupported MIME types and files larger than 10 MB are dropped server-side with a log warning rather than returning a 4xx. If an upload is required for your agent to do its job, validate payload.get("files") in before() and short-circuit the run when it's missing.

Supported File Types

Images

JPEG, PNG, GIF, WebP, HEIC/HEIF

Documents

PDF, Word, RTF, OpenDocument, EPUB, Plain text, HTML, Markdown

Data & Office

CSV, TSV, JSON, XML, Excel, PowerPoint

File size limit: Maximum 10 MB per file.

Authentication

Secure your webhooks with secret-based authentication. Inbound and sync webhooks require this secret by default. Turn off Require Authentication if a middleware before-function performs your authentication.

Providing the Secret

Each webhook has a unique secret key generated when created. When Require Authentication is enabled, provide it in three ways:

  • X-Connic-Secret Header (Recommended) - Most secure, secret is in headers
  • Authorization: Bearer Header - Standard OAuth-style bearer token
  • Query Parameter (?secret=...) - Less secure, visible in logs
terminal
# Using X-Connic-Secret header (recommended)
curl -X POST <webhook-url> \
  -H "X-Connic-Secret: <your-secret-key>" \
  -d '{"message": "hello"}'

# Using Authorization: Bearer header
curl -X POST <webhook-url> \
  -H "Authorization: Bearer <your-secret-key>" \
  -d '{"message": "hello"}'

# Using query parameter (less secure)
curl -X POST "<webhook-url>?secret=<your-secret-key>" \
  -d '{"message": "hello"}'

Error Responses

401

Invalid secret key

The provided secret doesn't match. Check your authentication.

404

Webhook not found

The webhook URL doesn't exist. Check the URL is correct.

400

Outbound-only webhook

This webhook is configured for outbound only and cannot be triggered.

Outbound Mode
Deliver agent results to external destinations. When agents complete, results are POSTed to your configured URL.

Setup Instructions

1

Set up an endpoint

Create an endpoint on your server that can receive POST requests.

2

Create the connector

Open your agent, click Add outbound connector, then Create New Connector and select HTTP Webhook.

3

Configure and create

Choose Outbound mode, enter the destination URL where Connic should POST results, optionally select a Bridge for private network access, and click Create. Results from linked agents will be POSTed to your URL with signature headers.

How Outbound Works

Outbound webhooks send agent results to an external URL when runs complete. You configure a destination URL and Connic POSTs the results there. For private endpoints, route outbound requests through a Connic Bridge. Best for sending results to external APIs, integrating with third-party services, and building event pipelines.

Note
Outbound-only webhooks cannot be triggered directly. They only send results when agents linked to them complete their runs.

Outbound Payload

What gets POSTed to your configured URL:

payload.json
{
  "run_id": "550e8400-e29b-41d4-a716-446655440000",
  "agent_name": "invoice-processor",
  "status": "completed",
  "output": "The invoice total is $1,234.56",
  "error": null,
  "started_at": "2024-01-15T10:30:00Z",
  "ended_at": "2024-01-15T10:30:05Z",
  "token_usage": {
    "prompt_tokens": 150,
    "candidates_tokens": 50,
    "total_tokens": 200
  }
}

Signature Verification

Every outbound webhook request includes an HMAC-SHA256 signature. Verify it to ensure requests are authentic.

Request Headers

  • X-Connic-Signature - Hex-encoded HMAC-SHA256 signature
  • X-Connic-Timestamp - Unix timestamp (seconds) when the request was signed

How to Verify

  1. Get the signing secret from your connector settings
  2. Extract the timestamp and signature from headers
  3. Concatenate timestamp + "." + raw request body
  4. Compute HMAC-SHA256 of the concatenated string using your secret
  5. Compare the computed signature with the received one
  6. Optionally check timestamp is within 5 minutes to prevent replay attacks

Python Example

verify_webhook.py
import hmac
import hashlib
import time

def verify_webhook(payload: bytes, signature: str, timestamp: str, secret: str) -> bool:
    """Verify a Connic webhook signature."""
    # Check timestamp is not too old (5 minute window)
    if abs(time.time() - int(timestamp)) > 300:
        return False
    
    # Build the signed payload
    signed_payload = f"{timestamp}.".encode() + payload
    
    # Compute expected signature
    expected = hmac.new(
        secret.encode(),
        signed_payload,
        hashlib.sha256
    ).hexdigest()
    
    return hmac.compare_digest(expected, signature)

# Usage in Flask
@app.route("/webhook", methods=["POST"])
def handle_webhook():
    signature = request.headers.get("X-Connic-Signature", "")
    timestamp = request.headers.get("X-Connic-Timestamp", "")
    
    if not verify_webhook(request.data, signature, timestamp, SIGNING_SECRET):
        return "Invalid signature", 401
    
    # Process the webhook...
    return "OK", 200

Node.js Example

verify_webhook.js
import crypto from 'crypto';

function verifyWebhook(payload, signature, timestamp, secret) {
  // Check timestamp is not too old (5 minute window)
  const now = Math.floor(Date.now() / 1000);
  if (Math.abs(now - parseInt(timestamp)) > 300) {
    return false;
  }
  
  // Build the signed payload
  const signedPayload = `${timestamp}.${payload}`;
  
  // Compute expected signature
  const expected = crypto
    .createHmac('sha256', secret)
    .update(signedPayload)
    .digest('hex');
  
  return crypto.timingSafeEqual(
    Buffer.from(expected),
    Buffer.from(signature)
  );
}

// Usage in Express
app.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => {
  const signature = req.headers['x-connic-signature'];
  const timestamp = req.headers['x-connic-timestamp'];
  
  if (!verifyWebhook(req.body.toString(), signature, timestamp, SIGNING_SECRET)) {
    return res.status(401).send('Invalid signature');
  }
  
  // Process the webhook...
  res.send('OK');
});
Security Best Practices
  • Always verify signatures in production
  • Use constant-time comparison to prevent timing attacks
  • Reject requests with timestamps older than 5 minutes
  • Store your signing secret securely (environment variables)
Sync Mode
Request-response pattern with immediate results. The webhook waits for the agent to finish and returns the result in the same response.

How Sync Works

Sync webhooks wait for the agent to complete and return the result in the same HTTP response. A traditional request-response pattern. Best for REST API integrations, interactive applications, and short-running tasks.

Timeout
Default timeout is 5 minutes. For longer tasks, use Inbound mode and poll for results.

Setup Instructions

1

Create the connector

Open your agent, click Add inbound connector in the Connector Flow, then Create New Connector and select HTTP Webhook.

2

Select Sync (Request-Response) and copy credentials

Choose the mode and click Create. The detail drawer opens automatically with the Webhook URL and Secret in the Configuration section. To view them later, hover over the connector in the Connector Flow and click the Eye icon.

3

Send requests

POST JSON to the webhook URL. The response blocks until the agent finishes and returns the result inline.

Response Format

response.json
{
  "status": "ok",
  "result": {
    "run_id": "550e8400-e29b-41d4-a716-446655440000",
    "agent_name": "invoice-processor",
    "status": "completed",
    "output": "The invoice total is $1,234.56",
    "error": null
  }
}

Client Examples

sync_request.py
import requests

url = "<webhook-url>"
secret = "<your-secret-key>"

payload = {"query": "What is the status of order 123?"}
resp = requests.post(
    url,
    json=payload,
    headers={"X-Connic-Secret": secret},
    timeout=60,
)
resp.raise_for_status()
print(resp.json()["result"]["output"])
sync_request.js
import fetch from "node-fetch";

const url = "<webhook-url>";
const secret = "<your-secret-key>";

const resp = await fetch(url, {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "X-Connic-Secret": secret,
  },
  body: JSON.stringify({ query: "Summarize the latest ticket" }),
});

if (!resp.ok) throw new Error(await resp.text());
const data = await resp.json();
console.log(data.result.output);

Sending Requests

Webhooks accept both GET and POST requests with various content types.

JSON Requests

The most common way to trigger webhooks. Send a POST request with a JSON body (application/json). The entire JSON payload is passed to your agent as input.

terminal
curl -X POST <webhook-url> \
  -H "Content-Type: application/json" \
  -H "X-Connic-Secret: <your-secret-key>" \
  -d '{
    "prompt": "Analyze this data",
    "data": {"key": "value"}
  }'

Form Data Requests

Useful for simple integrations or when your source system sends form data (application/x-www-form-urlencoded). Each form field becomes a key-value pair in the payload.

terminal
curl -X POST <webhook-url> \
  -H "X-Connic-Secret: <your-secret-key>" \
  -F "message=Process this invoice" \
  -F "customer_id=12345"

GET Requests

For simple triggers, use GET requests with query parameters. All query parameters (except secret) are passed to the agent.

terminal
curl "<webhook-url>?secret=<your-secret-key>&prompt=hello"

Get Your Webhook URL & Secret

The webhook URL includes your webhook_id, and theX-Connic-Secret is generated when you create the connector. Authentication is enabled by default. Disable Require Authentication only when your middleware validates requests before they reach Connic.

  1. Open your agent's detail page in the Agents section
  2. Add a webhook connector via the + button on the connector flow, or select an existing one
  3. Click the eye icon on the connector to open its details
  4. Copy the Webhook URL and Secret from the connector details

File Uploads

Send files to your agents using multipart/form-data. Files are passed directly to the LLM for analysis.

Files are extracted and sent to the model as inline data:

terminal
curl -X POST <webhook-url> \
  -H "X-Connic-Secret: <your-secret-key>" \
  -F "instructions=What is the total of this invoice?" \
  -F "file=@/path/to/invoice.pdf"

Upload multiple files in a single request:

terminal
curl -X POST <webhook-url> \
  -H "X-Connic-Secret: <your-secret-key>" \
  -F "message=Compare these two documents" \
  -F "file1=@document1.pdf" \
  -F "file2=@document2.pdf"

Multipart requests land in context["payload"] as a normalised dict with text parts as top-level keys and uploads under files (each file's bytes are base64-encoded). The LLM-facing content is reconstructed automatically — each file becomes a binary part, and the leading text part is the payload with only files removed:

payload shape
# context["payload"] inside middleware before()
{
  "customer_id": "12345",      # multipart text fields sit at the top level
  "tag": ["a", "b"],           # repeated keys are grouped into a list
  "message": "Process this invoice",
  "files": [
    {
      "name": "invoice.pdf",     # original filename
      "field_name": "file",      # form field name
      "mime_type": "application/pdf",
      "data": "<base64-bytes>",  # always base64-encoded ASCII
      "size": 12345              # bytes
    }
  ]
}

# If no supported file made it through, payload collapses to just the
# top-level form fields — no "files" key.

Unsupported MIME types and files larger than 10 MB are dropped server-side with a log warning rather than returning a 4xx. If an upload is required for your agent to do its job, validate payload.get("files") in before() and short-circuit the run when it's missing.

Supported File Types

Images

JPEG, PNG, GIF, WebP, HEIC/HEIF

Documents

PDF, Word, RTF, OpenDocument, EPUB, Plain text, HTML, Markdown

Data & Office

CSV, TSV, JSON, XML, Excel, PowerPoint

File size limit: Maximum 10 MB per file.

Authentication

Secure your webhooks with secret-based authentication. Inbound and sync webhooks require this secret by default. Turn off Require Authentication if a middleware before-function performs your authentication.

Providing the Secret

Each webhook has a unique secret key generated when created. When Require Authentication is enabled, provide it in three ways:

  • X-Connic-Secret Header (Recommended) - Most secure, secret is in headers
  • Authorization: Bearer Header - Standard OAuth-style bearer token
  • Query Parameter (?secret=...) - Less secure, visible in logs
terminal
# Using X-Connic-Secret header (recommended)
curl -X POST <webhook-url> \
  -H "X-Connic-Secret: <your-secret-key>" \
  -d '{"message": "hello"}'

# Using Authorization: Bearer header
curl -X POST <webhook-url> \
  -H "Authorization: Bearer <your-secret-key>" \
  -d '{"message": "hello"}'

# Using query parameter (less secure)
curl -X POST "<webhook-url>?secret=<your-secret-key>" \
  -d '{"message": "hello"}'

Error Responses

401

Invalid secret key

The provided secret doesn't match. Check your authentication.

404

Webhook not found

The webhook URL doesn't exist. Check the URL is correct.

400

Outbound-only webhook

This webhook is configured for outbound only and cannot be triggered.