Goals Planned
Goals turn chat into a measurable funnel. Define a conversion (signup, trial, purchase, demo booked) and report on how many conversations led to that outcome, attributed by visitor and agent. Used to prove the ROI of customer-facing chat.
This endpoint is on the roadmap. The API surface may change before launch. For early access, contact us; we are running a closed alpha with a handful of customers.
Goal kinds
| Kind | Matcher | Used when |
|---|---|---|
event | { event: "name" } | You fire a custom event via the widget JS API or the REST /events endpoint. |
url | { urlPattern: "/checkout/success" } | Visitor lands on a thank-you URL within 24h of the conversation. |
tag | { tag: "converted" } | An agent tags the conversation manually. |
revenue | { minValue: 1 } | External system POSTs a revenue event via /goals/:id/attribute. |
List goals
GET/api/v1/goalsAuth: Bearer
Code samples
cURL
curl 'https://app.message.com/api/v1/goals' \
-H 'Authorization: Bearer YOUR_WORKSPACE_JWT'JavaScript
const res = await fetch('https://app.message.com/api/v1/goals', {
headers: { Authorization: 'Bearer ' + token }
});
const { goals } = await res.json();Python
import requests
r = requests.get(
"https://app.message.com/api/v1/goals",
headers={"Authorization": f"Bearer {token}"},
)
goals = r.json()["goals"]Ruby
require "net/http"
require "json"
uri = URI("https://app.message.com/api/v1/goals")
res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) do |http|
req = Net::HTTP::Get.new(uri)
req["Authorization"] = "Bearer #{token}"
http.request(req)
end
goals = JSON.parse(res.body)["goals"]PHP
<?php
$ctx = stream_context_create([
"http" => ["method" => "GET", "header" => "Authorization: Bearer $token"]
]);
$goals = json_decode(file_get_contents("https://app.message.com/api/v1/goals", false, $ctx), true)["goals"];Get one goal
GET/api/v1/goals/:idAuth: Bearer
200 OK
{
"id": "uuid",
"name": "Trial signup",
"kind": "event",
"matcher": { "event": "trial_started" },
"value": null,
"currency": null,
"active": true,
"createdAt": "2026-05-13T15:30:00Z"
}Create a goal
POST/api/v1/goalsAuth: Bearer
| Field | Type | Description |
|---|---|---|
| namerequired | string | Display name. Shown in reports. |
| kindrequired | enum | One of event, url, tag, revenue. |
| matcherrequired | object | Match config; shape depends on kind. |
| valueoptional | number | Static value attributed when the goal fires (e.g., $20 per signup). |
| currencyoptional | string | ISO 4217 currency for the value field. |
Body
{
"name": "Trial signup",
"kind": "event",
"matcher": { "event": "trial_started" }
}Attribute a conversion
POST/api/v1/goals/:id/attributeAuth: Bearer
For revenue goals, your backend POSTs the actual value when a conversion fires (e.g., from a Stripe webhook). Pass conversationId if you have it; otherwise we attribute by visitorId.
Body
{
"conversationId": "uuid",
"value": 49.00,
"currency": "USD",
"metadata": { "stripeChargeId": "ch_123" }
}Goal report
GET/api/v1/goals/:id/reportAuth: Bearer
Returns conversions over a date range, optionally broken down by agent, department, channel, or visitor segment. See Reports API for output shape.
Errors
| Code | When |
|---|---|
400 invalid_matcher | Matcher shape does not align with goal kind. |
402 plan_limit | Workspace plan does not include goals. |
404 not_found | Goal not in this workspace. |
Common pitfalls
- Defining a goal on a vanity URL. The thank-you page must be unique to the conversion. If
/checkout/successis also reachable from a marketing campaign, your goal over-counts. - Firing the same event twice. We de-dupe by visitorId + event name + 1-hour window. Build an explicit event payload (e.g.,
orderId) if you need finer granularity. - Forgetting currency on revenue goals. Default is USD. Mixed-currency aggregates require explicit currency on every attribution.