Track event
curl --request POST \
--url https://api.example.com/api/v1/events \
--header 'Content-Type: application/json' \
--data '
{
"external_user_id": "<string>",
"name": "<string>",
"properties": {}
}
'import requests
url = "https://api.example.com/api/v1/events"
payload = {
"external_user_id": "<string>",
"name": "<string>",
"properties": {}
}
headers = {"Content-Type": "application/json"}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({external_user_id: '<string>', name: '<string>', properties: {}})
};
fetch('https://api.example.com/api/v1/events', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.example.com/api/v1/events",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'external_user_id' => '<string>',
'name' => '<string>',
'properties' => [
]
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.example.com/api/v1/events"
payload := strings.NewReader("{\n \"external_user_id\": \"<string>\",\n \"name\": \"<string>\",\n \"properties\": {}\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.example.com/api/v1/events")
.header("Content-Type", "application/json")
.body("{\n \"external_user_id\": \"<string>\",\n \"name\": \"<string>\",\n \"properties\": {}\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.example.com/api/v1/events")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Content-Type"] = 'application/json'
request.body = "{\n \"external_user_id\": \"<string>\",\n \"name\": \"<string>\",\n \"properties\": {}\n}"
response = http.request(request)
puts response.read_body{
"data.accepted": true
}Events
Track event
POST /api/v1/events — emit a single user event.
POST
/
api
/
v1
/
events
Track event
curl --request POST \
--url https://api.example.com/api/v1/events \
--header 'Content-Type: application/json' \
--data '
{
"external_user_id": "<string>",
"name": "<string>",
"properties": {}
}
'import requests
url = "https://api.example.com/api/v1/events"
payload = {
"external_user_id": "<string>",
"name": "<string>",
"properties": {}
}
headers = {"Content-Type": "application/json"}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({external_user_id: '<string>', name: '<string>', properties: {}})
};
fetch('https://api.example.com/api/v1/events', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.example.com/api/v1/events",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'external_user_id' => '<string>',
'name' => '<string>',
'properties' => [
]
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.example.com/api/v1/events"
payload := strings.NewReader("{\n \"external_user_id\": \"<string>\",\n \"name\": \"<string>\",\n \"properties\": {}\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.example.com/api/v1/events")
.header("Content-Type", "application/json")
.body("{\n \"external_user_id\": \"<string>\",\n \"name\": \"<string>\",\n \"properties\": {}\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.example.com/api/v1/events")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Content-Type"] = 'application/json'
request.body = "{\n \"external_user_id\": \"<string>\",\n \"name\": \"<string>\",\n \"properties\": {}\n}"
response = http.request(request)
puts response.read_body{
"data.accepted": true
}Emit a single event for a user. The endpoint accepts both the 10 canonical lifecycle event names and any custom name you choose.
Request
POST /api/v1/events HTTP/1.1
Host: api.vibefollow.com
Authorization: Bearer sk_live_••••
Content-Type: application/json
Idempotency-Key: 7b6c7e9e-2c2d-4a8d-9d3a-4f3d2c1b0a99
Body
{
"external_user_id": "usr_42",
"name": "user_signed_up",
"properties": {
"plan": "trial",
"source": "organic"
}
}
string
required
External user ID. Should match the ID used in Upsert user. If the user hasn’t been identified yet, the event is buffered and attached when they’re first seen.
string
required
Event name. One of the canonical 10 or any custom string (snake_case recommended).
object
JSON-serialisable property bag. Optional — defaults to
{}.Response
HTTP/1.1 202 Accepted
Content-Type: application/json
{ "data": { "accepted": true } }
boolean
Always
true on success. The event is enqueued and processed asynchronously.Examples
import { VibeFollow } from '@vibefollow/sdk';
const vf = new VibeFollow({ apiKey: process.env.VIBEFOLLOW_API_KEY! });
// Typed helper for canonical events
await vf.users.signedUp('usr_42', { plan: 'trial', source: 'organic' });
// Generic track for custom events
await vf.events.track('dashboard_created', 'usr_42', {
source: 'wizard',
});
curl https://api.vibefollow.com/api/v1/events \
-X POST \
-H "Authorization: Bearer $VIBEFOLLOW_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{
"external_user_id": "usr_42",
"name": "user_signed_up",
"properties": { "plan": "trial", "source": "organic" }
}'
Common errors
401 auth_required
401 auth_required
Missing or malformed
Authorization header.403 forbidden
403 forbidden
API key valid but not authorised for this resource.
422 validation_failed
422 validation_failed
Body failed schema validation;
errors[0].field indicates the offending field.429 rate_limited
429 rate_limited
Wait
Retry-After seconds.5xx server_error
5xx server_error
Retry — the
Idempotency-Key prevents duplicates.