message.comDevelopers

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

KindMatcherUsed 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
FieldTypeDescription
namerequiredstringDisplay name. Shown in reports.
kindrequiredenumOne of event, url, tag, revenue.
matcherrequiredobjectMatch config; shape depends on kind.
valueoptionalnumberStatic value attributed when the goal fires (e.g., $20 per signup).
currencyoptionalstringISO 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

CodeWhen
400 invalid_matcherMatcher shape does not align with goal kind.
402 plan_limitWorkspace plan does not include goals.
404 not_foundGoal 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/success is 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.