ResourcesClient Examples

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

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)

There is no published 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 momentCallScope needed
User logs inPOST /people/{userId}/devices with the FCM token — see Devicesdevices:register
Something happensPOST /track with {external_id, event, parameters}events:write
Screen viewPOST /track (an ordinary event, e.g. screen_viewed)events:write
FCM rotates the tokenPOST /people/{userId}/devices again with the new tokendevices:register
User logs outDELETE /people/{userId}/devices/{token}devices:register

parameters is the wire field name — properties is accepted as an alias but parameters is canonical.

There is no client-side event filter. Send everything, the same as any other analytics SDK — campaigns are authored in the Flameup dashboard, and triggers match the event name exactly, so an event only starts a journey when a campaign names it precisely. Deciding what matters is a dashboard change, not an app release.
Wire 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

ScopeRegister / unregisterList devices
devices:registerYesNo — deliberately excluded
devices:writeYesYes
devices:readNoYes

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

StatusMeaning
400Malformed body. Check token (10–4096 chars) and platform
401 / 403Key rejected, or it lacks the scope for that route
409Person is inactive
429Rate limited — back off and retry
Push delivery also requires your workspace's FCM credentials in the dashboard (Settings → Messaging Channels → Push). Until that is done, registration and journeys work normally and simply stop at the delivery step.

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.

Do not generate clients from 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