Why use the Custom API?
The Custom API lets you send signals to ProductBet from any source that isn't covered by a built-in integration. Use it to connect internal tools, scripts, CRMs, or any system that can make HTTP requests.
Getting started
- Go to Settings > Integrations > Custom API and click Generate API Key.
- Copy your API key. It won't be shown again.
- Send a POST request to your ingest endpoint.
Endpoint
POST https://<your-deployment>.convex.site/ingest
Your endpoint URL is shown on the Custom API integration page after connecting.
Authentication
Include your API key in the Authorization header:
Authorization: Bearer pb_live_...
Request body
Send a JSON object (single signal) or an array of objects (batch, up to 100).
Required fields
| Field | Type | Description |
|---|---|---|
id | string | Unique identifier for this signal. Used for deduplication — sending the same id twice updates the existing signal instead of creating a new one. |
content | string | The text content to analyze. This is what the AI processes to extract insights. |
Optional fields
| Field | Type | Description |
|---|---|---|
url | string | Link back to the source (e.g. a ticket URL). Shown on the signal detail view. |
tags | string[] | Tags to attach to the signal. |
timestamp | number | Unix timestamp in milliseconds. Defaults to the current time. |
metadata | object | Arbitrary key-value data stored alongside the signal. |
Classification overrides
By default, ProductBet's AI classifies each signal's type, sentiment, actor, and topic. You can override any of these fields if your source system already knows the classification. When provided, these values are used directly and the AI result for that field is ignored.
| Field | Type | Allowed values |
|---|---|---|
type | string | feature_request, bug, support_ticket, deal_lost, deal_won, churn_signal |
sentiment | string | positive, negative |
actor | string | customer, prospect, churned_user, team_member, partner |
topic | string | onboarding, core_workflow, reporting, integrations, search, notifications, admin, pricing, security, compliance, billing, support, competition, documentation, other |
The AI still extracts the summary and goal fields regardless of overrides.
Examples
Minimal signal
curl -X POST https://<your-deployment>.convex.site/ingest \
-H "Authorization: Bearer pb_live_..." \
-H "Content-Type: application/json" \
-d '{"id": "ticket-1234", "content": "Customer wants dark mode support"}'
Signal with classification overrides
curl -X POST https://<your-deployment>.convex.site/ingest \
-H "Authorization: Bearer pb_live_..." \
-H "Content-Type: application/json" \
-d '{
"id": "ticket-1234",
"content": "Customer wants dark mode support",
"type": "feature_request",
"sentiment": "negative",
"actor": "customer",
"topic": "core_workflow",
"url": "https://support.example.com/tickets/1234",
"tags": ["ui", "theming"]
}'
Batch request
curl -X POST https://<your-deployment>.convex.site/ingest \
-H "Authorization: Bearer pb_live_..." \
-H "Content-Type: application/json" \
-d '[
{"id": "fb-001", "content": "Login is too slow", "type": "bug"},
{"id": "fb-002", "content": "Love the new dashboard", "sentiment": "positive"}
]'
Response
Success (200)
{"success": true, "scheduled": 1}
Validation error (400)
{"error": "body.type must be one of: feature_request, bug, support_ticket, deal_lost, deal_won, churn_signal"}
Authentication error (401)
{"error": "Invalid API key"}
Deduplication
The id field is your deduplication key. Sending a signal with the same id updates the existing record instead of creating a duplicate. Use stable, unique identifiers from your source system (e.g. ticket IDs, feedback IDs).
Troubleshooting
- Signal not appearing? Make sure you're sending a unique
idfor each distinct signal. Reusing the sameidupdates the existing signal. - 400 error on classification fields? Check that the value matches one of the allowed values listed above. Values are case-sensitive.
- 401 error? Verify your API key starts with
pb_live_and is included in theAuthorization: Bearerheader.