Client Examples
Copy-paste HTTP clients for integrating with Flameup
Overview
There is no Flameup SDK to install. No package is published on npm, PyPI,
pub.dev, CocoaPods or Maven. Integration is plain HTTPS against
https://api.flameup.ai/api/v1, which needs nothing beyond an HTTP client you
already have.
What follows are thin, dependency-light JavaScript and Python clients you can copy into your codebase and edit, plus the call-level recipe for Flutter. They are examples, not a supported library — you own the copy.
REST API
All Flameup functionality is available through our REST API. Use standard HTTP clients in any language:
JavaScript / Node.js
Using Fetch (Recommended)
Modern JavaScript includes fetch by default:
// flare.js - Simple Flameup wrapper
class Flameup {
constructor(apiKey, workspaceId) {
this.apiKey = apiKey;
this.workspaceId = workspaceId;
this.baseUrl = 'https://api.flameup.ai/api/v1';
}
// Workspace-scoped request (used by push and other automation endpoints)
async request(endpoint, options = {}) {
const url = `${this.baseUrl}/workspaces/${this.workspaceId}${endpoint}`;
return this._send(url, options);
}
// Workspace-agnostic request (the People API-key surface lives at /api/v1/people)
async apiRequest(endpoint, options = {}) {
const url = `${this.baseUrl}${endpoint}`;
return this._send(url, options);
}
async _send(url, options = {}) {
const response = await fetch(url, {
...options,
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${this.apiKey}`,
...options.headers
}
});
if (!response.ok) {
const error = await response.json();
throw new Error(error.error?.message || 'Request failed');
}
return response.json();
}
// People (API key targets the workspace-agnostic /api/v1/people path)
async identifyUser(userId, traits = {}) {
return this.apiRequest('/people', {
method: 'POST',
body: JSON.stringify({ userId, traits })
});
}
async getUser(userId) {
// userId is the external user ID you assigned; returns the person object
return this.apiRequest(`/people/${userId}`);
}
// Events (uses /api/v1/track endpoint directly)
async track(externalId, event, parameters = {}) {
const response = await fetch('https://api.flameup.ai/api/v1/track', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${this.apiKey}`
},
body: JSON.stringify({
external_id: externalId,
event,
parameters,
timestamp: new Date().toISOString()
})
});
return response.json();
}
// Push notifications
async sendPush(externalId, title, body, options = {}) {
return this.request('/push/send', {
method: 'POST',
body: JSON.stringify({
external_id: externalId,
title,
body,
...options
})
});
}
}
// Usage
const flare = new Flameup('ws_live_abc12345_abc123.your_secret_here', 'ws_abc123');
await flare.identifyUser('user_123', {
email: 'jane@example.com',
first_name: 'Jane'
});
await flare.track('user_123', 'page_viewed', {
page: '/pricing'
});
Using Axios
import axios from 'axios';
const flare = axios.create({
baseURL: 'https://api.flameup.ai/api/v1',
headers: {
'Authorization': 'Bearer ws_live_abc12345_abc123.your_secret_here'
}
});
// Identify user (People API key surface is workspace-agnostic)
await flare.post('/people', {
userId: 'user_123',
email: 'jane@example.com',
traits: { first_name: 'Jane' }
});
// Track event (uses /track endpoint)
await flare.post('/track', {
external_id: 'user_123',
event: 'purchase_completed',
parameters: { amount: 99.99 }
});
Python
Using Requests
import requests
from datetime import datetime
class Flameup:
def __init__(self, api_key, workspace_id):
self.api_key = api_key
self.workspace_id = workspace_id
self.base_url = 'https://api.flameup.ai/api/v1'
self.session = requests.Session()
self.session.headers.update({
'Content-Type': 'application/json',
'Authorization': f'Bearer {api_key}'
})
def _url(self, endpoint):
# Workspace-scoped URL, used by push and other automation endpoints
return f'{self.base_url}/workspaces/{self.workspace_id}{endpoint}'
def identify(self, user_id, traits=None):
"""Create or update a user profile (People API key surface is workspace-agnostic)."""
return self.session.post(
f'{self.base_url}/people',
json={'userId': user_id, 'traits': traits or {}}
).json()
def track(self, external_id, event, parameters=None):
"""Track a user event (uses /api/v1/track endpoint)."""
return self.session.post(
f'{self.base_url}/track',
json={
'external_id': external_id,
'event': event,
'parameters': parameters or {},
'timestamp': datetime.utcnow().isoformat() + 'Z'
}
).json()
def send_push(self, external_id, title, body, **options):
"""Send a push notification."""
return self.session.post(
self._url('/push/send'),
json={
'external_id': external_id,
'title': title,
'body': body,
**options
}
).json()
# Usage
flare = Flameup('ws_live_abc12345_abc123.your_secret_here', 'ws_abc123')
flare.identify('user_123', {
'email': 'jane@example.com',
'first_name': 'Jane'
})
flare.track('user_123', 'page_viewed', {'page': '/pricing'})
Using HTTPX (Async)
import httpx
from datetime import datetime
class AsyncFlameup:
def __init__(self, api_key, workspace_id):
self.api_key = api_key
self.workspace_id = workspace_id
self.base_url = 'https://api.flameup.ai/api/v1'
self.client = httpx.AsyncClient(
headers={
'Content-Type': 'application/json',
'Authorization': f'Bearer {api_key}'
}
)
async def identify(self, user_id, traits=None):
# People API key surface is workspace-agnostic (/api/v1/people)
response = await self.client.post(
f'{self.base_url}/people',
json={'userId': user_id, 'traits': traits or {}}
)
return response.json()
async def track(self, external_id, event, parameters=None):
response = await self.client.post(
f'{self.base_url}/track',
json={
'external_id': external_id,
'event': event,
'parameters': parameters or {},
'timestamp': datetime.utcnow().isoformat() + 'Z'
}
)
return response.json()
# Usage
import asyncio
async def main():
flare = AsyncFlameup('ws_live_abc12345_abc123.your_secret_here', 'ws_abc123')
await flare.identify('user_123', {'email': 'jane@example.com'})
await flare.track('user_123', 'page_viewed', {'page': '/pricing'})
asyncio.run(main())
Mobile
Flutter (Dart)
flameup_flutter package, and no
Dart file to download from these docs yet. A ready-made single-file client
(built on plain HTTPS plus firebase_messaging, with a Firebase-Analytics-like
surface) exists and is available from Flameup on request while publication
is pending — contact support@flameup.ai. Until then, the section below is the
integration contract itself: the four HTTP calls a Flutter app makes, which any
Dart developer can wire with the http package in an afternoon.Dependencies:
# pubspec.yaml
dependencies:
firebase_core: ^3.0.0
firebase_messaging: ^15.0.0
http: ^1.2.0
Android also needs android/app/google-services.json from your Firebase project.
The calls a Flutter app makes
Authenticate every call with your pk_live_ public key
(Authorization: Bearer ...). All identifiers are your own user id — the
same value your backend sends to /identify; there is no Flameup UUID to
fetch, cache, or pass around.
| App moment | Call | Scope needed |
|---|---|---|
| User logs in | POST /people/{userId}/devices with the FCM token — see Devices | devices:register |
| Something happens | POST /track with {external_id, event, parameters} | events:write |
| Screen view | POST /track (an ordinary event, e.g. screen_viewed) | events:write |
| FCM rotates the token | POST /people/{userId}/devices again with the new token | devices:register |
| User logs out | DELETE /people/{userId}/devices/{token} | devices:register |
parameters is the wire field name — properties is accepted as an alias but parameters is canonical.
onTokenRefresh, not just getToken(). FCM rotates device tokens, and a stale token drops every push silently — with no error anywhere in your logs.Unregister the device before clearing local login state. Skip it and the next person to use that handset keeps receiving the previous user's notifications.
Traits
A public key can call /identify, if it holds people:identify. That scope
is narrow in one sense only: it authorizes this one route and nothing else,
where people:write would also open the rest of the People API.
Be clear about what it does not do. people:identify gates the route; it
does not restrict which person you may write or which trait keys you may
set. The request names the person by userId (with external_id and user_id
accepted as aliases), so a key that ships inside your app can write any trait on
any person whose identifier someone can guess or read out of your own API
responses. Nothing in the request proves the install
belongs to that person.
That is why backend-owned traits — plan, entitlements, lifetime value — should still be written by your backend with a secret key. Not because the public key is forbidden from writing them, but because anything your app can send, someone holding your extracted key can also send. Journeys branch on those traits, so a forged write reroutes a real user's messages.
Keep client-sent traits to the ones the device is genuinely the source of and that carry no routing weight on their own: locale, app version, notification permission.
Required scope
| Scope | Register / unregister | List devices |
|---|---|---|
devices:register | Yes | No — deliberately excluded |
devices:write | Yes | Yes |
devices:read | No | Yes |
The three scopes a shipped app may hold are events:write, devices:register
and people:identify. A public key is rejected at creation if you ask for
anything else.
devices:register exists so an app can enrol its own device without being able to enumerate every device token in the workspace. Use it in a shipped app, never devices:write.
Errors worth handling
| Status | Meaning |
|---|---|
400 | Malformed body. Check token (10–4096 chars) and platform |
401 / 403 | Key rejected, or it lacks the scope for that route |
409 | Person is inactive |
429 | Rate limited — back off and retry |
platform must be exactly ios, android, or web. Check kIsWeb before defaultTargetPlatform — the latter reports the host OS on web builds and would register a browser on a Mac as ios.
cURL Examples
For quick testing or shell scripts:
# Set your credentials
export FLARE_API_KEY="ws_live_abc12345_abc123.your_secret_here"
export WORKSPACE_ID="ws_abc123"
# Identify a user (People API key surface is workspace-agnostic)
curl -X POST "https://api.flameup.ai/api/v1/people" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $FLARE_API_KEY" \
-d '{"userId": "user_123", "email": "jane@example.com"}'
# Track an event
curl -X POST "https://api.flameup.ai/api/v1/track" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $FLARE_API_KEY" \
-d '{"external_id": "user_123", "event": "test_event"}'
OpenAPI Specification
Use our OpenAPI spec to generate clients in any language.
GET /api/v1/docs/openapi. That endpoint returns a minimal internal stub covering only /health, /events, and /workspaces — it does not describe People, Devices, Track, or Campaigns, so a generated client will be missing nearly the whole API.- Spec: The complete specification is the one backing the API Reference tab of these docs. Use it as your generator input.
- Generators: Use OpenAPI Generator or similar tools
# Generate a TypeScript client from the full spec
openapi-generator generate \
-i api-reference/openapi.yaml \
-g typescript-fetch \
-o ./flare-client