API Docs
JSON

Stactics API

Send lightweight analytics events from your apps to Stactics.

Bearer Auth

Use a project ingest key in the Authorization header.

Limit Aware

Monthly event limits are enforced by account plan.

Aggregated

Events feed backend-owned metrics and identity linking.

SDKs

Android Java, Angular, Go, JavaScript, Kotlin, Next.js, React, Ruby, Rust, and Swift clients for the ingest API
Client SDK

Choose your integration

Android Java

Android Java client for mobile apps using a public ingest key.

Install implementation "app.stactics:stactics-android:0.1.0"
import app.stactics.StacticsClient;
import java.util.Map;

StacticsClient stactics = new StacticsClient(
    BuildConfig.STACTICS_PUBLIC_KEY
);

stactics.track(
    "signup",
    "user_123",
    "production",
    Map.of("plan", "free")
);

Angular

Angular provider and service wrapper around the JavaScript client.

Install npm install @stactics.io/angular @stactics.io/js
import { provideStactics } from "@stactics.io/angular";

bootstrapApplication(AppComponent, {
  providers: [
    provideStactics({
      apiKey: "pk_...",
      environment: "production"
    })
  ]
});

Go

Backend Go client for trusted services, workers, and API integrations.

Install go get github.com/nexuindustries/stactics/sdks/go/stactics
package main

import (
  "context"
  "os"

  stactics "github.com/nexuindustries/stactics/sdks/go/stactics"
)

func main() {
  client := stactics.NewClient(os.Getenv("STACTICS_SECRET_KEY"))

  _, _ = client.Track(context.Background(), "signup", stactics.Event{
    UserID:      "user_123",
    EnvironmentID: "00000000-0000-0000-0000-000000000000",
    Metadata: map[string]any{
      "plan": "free",
    },
  })
}

JavaScript

Universal JavaScript client for browsers, Node, and framework apps.

Install npm install @stactics.io/js
import { StacticsClient, StacticsEvents } from "@stactics.io/js";

const stactics = new StacticsClient({
  apiKey: "pk_..."
});

await stactics.trackEvent(StacticsEvents.signup({
  userId: "user_123",
  environmentId: "00000000-0000-0000-0000-000000000000",
  metadata: { plan: "free" }
}));

Kotlin

Kotlin client for Android apps and JVM services using the public ingest API.

Install implementation("app.stactics:stactics-android:0.1.0")
import app.stactics.StacticsClient

val stactics = StacticsClient(
    apiKey = BuildConfig.STACTICS_PUBLIC_KEY
)

fun trackSignup() {
    stactics.track(
        eventType = "signup",
        userId = "user_123",
        environmentId = "00000000-0000-0000-0000-000000000000",
        metadata = mapOf("plan" to "free")
    )
}

Next.js

Server-side route handler integration for Next.js apps using a secret ingest key.

Install npm install @stactics.io/js
import { StacticsClient, StacticsEvents } from "@stactics.io/js";
import { NextResponse } from "next/server";

const stactics = new StacticsClient({
  apiKey: process.env.STACTICS_SECRET_KEY
});

export async function POST(request: Request) {
  const body = await request.json();

  await stactics.trackEvent(StacticsEvents.signup({
    userId: body.userId,
    environmentId: process.env.STACTICS_ENVIRONMENT_ID,
    metadata: { plan: body.plan }
  }));

  return NextResponse.json({ ok: true });
}

React

Client-side React integration for browser apps using a public ingest key.

Install npm install @stactics.io/js
import { useMemo } from "react";
import { StacticsClient, StacticsEvents } from "@stactics.io/js";

export function SignupButton({ userId }) {
  const stactics = useMemo(() => new StacticsClient({
    apiKey: import.meta.env.VITE_STACTICS_PUBLIC_KEY
  }), []);

  async function trackSignup() {
    await stactics.trackEvent(StacticsEvents.signup({
      userId,
      environmentId: import.meta.env.VITE_STACTICS_ENVIRONMENT_ID,
      metadata: { source: "signup_button" }
    }));
  }

  return <button onClick={trackSignup}>Create account</button>;
}

Ruby

Server-side Ruby client for Rails, jobs, and trusted backend integrations.

Install gem install stactics
require "stactics"

client = Stactics::Client.new(
  api_key: ENV.fetch("STACTICS_SECRET_KEY")
)

client.track(
  "signup",
  user_id: "user_123",
  environment_id: "00000000-0000-0000-0000-000000000000",
  metadata: { plan: "free" }
)

Rust

Rust client for trusted backend services and jobs using a secret ingest key.

Install cargo add stactics
use stactics::{Client, Event};
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), stactics::Error> {
    let client = Client::new(std::env::var("STACTICS_SECRET_KEY").unwrap());

    client.track(Event::new("signup")
        .user_id("user_123")
        .environment_id("00000000-0000-0000-0000-000000000000")
        .metadata(json!({ "plan": "free" })))
        .await?;

    Ok(())
}

Swift

Swift client for iOS and server-side Swift apps using public or secret ingest keys.

Install https://github.com/nexuindustries/stactics/tree/main/sdks/swift
import Stactics

let client = StacticsClient(
    apiKey: ProcessInfo.processInfo.environment["STACTICS_PUBLIC_KEY"] ?? ""
)

Task {
    try await client.track(
        "signup",
        userId: "user_123",
        environmentId: "00000000-0000-0000-0000-000000000000",
        metadata: ["plan": "free"]
    )
}
SDK calls send JSON to POST /v1/events and POST /v1/events/batch. Client-side apps should use pk_... keys; server-side apps should use sk_... keys.

Default Event Catalog

Recommended payloads for every Stactics predefined event type
signup Signup Send when a user creates an account.

Recommended fields: user_id, email, display_name, account_id, device_id, platform, metadata.plan.

{
  "event_type": "signup",
  "user_id": "user_123",
  "account_id": "team_123",
  "email": "founder@example.com",
  "display_name": "Sam Founder",
  "platform": "web",
  "metadata": {
    "plan": "free"
  }
}
login Login / identity Send after successful authentication.

Recommended fields: user_id, email, display_name, device_id, platform.

{
  "event_type": "login",
  "user_id": "user_123",
  "email": "founder@example.com",
  "display_name": "Sam Founder",
  "device_id": "install_abc",
  "platform": "ios"
}
active Active user Send for a meaningful active-user heartbeat or session qualification.

Recommended fields: user_id or device_id, platform, metadata.session_id.

{
  "event_type": "active",
  "user_id": "user_123",
  "platform": "web",
  "metadata": {
    "session_id": "session_123"
  }
}
purchase Purchase / revenue Send after successful payment. Include amount_cents and currency for revenue reporting.

Recommended fields: amount_cents, currency, user_id, account_id, metadata.transaction_id, metadata.product_id.

{
  "event_type": "purchase",
  "user_id": "user_123",
  "account_id": "team_123",
  "amount_cents": 1299,
  "currency": "AUD",
  "metadata": {
    "transaction_id": "txn_123",
    "product_id": "pro_monthly"
  }
}
subscription_started Subscription started Send when a paid subscription starts. Include amount_cents and currency for revenue reporting.

Recommended fields: user_id, account_id, amount_cents, currency, metadata.plan, metadata.interval.

{
  "event_type": "subscription_started",
  "user_id": "user_123",
  "account_id": "team_123",
  "amount_cents": 2900,
  "currency": "AUD",
  "metadata": {
    "plan": "pro",
    "interval": "month"
  }
}
subscription_cancelled Subscription cancelled Send when a subscription is cancelled.

Recommended fields: user_id, account_id, metadata.plan, metadata.reason.

{
  "event_type": "subscription_cancelled",
  "user_id": "user_123",
  "account_id": "team_123",
  "metadata": {
    "plan": "pro",
    "reason": "customer_request"
  }
}
trial_started Trial started Send when a trial starts.

Recommended fields: user_id, account_id, metadata.plan, metadata.trial_days.

{
  "event_type": "trial_started",
  "user_id": "user_123",
  "account_id": "team_123",
  "metadata": {
    "plan": "pro",
    "trial_days": 14
  }
}
trial_converted Trial converted Send when a trial becomes paid. Include amount_cents and currency for revenue reporting.

Recommended fields: user_id, account_id, amount_cents, currency, metadata.plan.

{
  "event_type": "trial_converted",
  "user_id": "user_123",
  "account_id": "team_123",
  "amount_cents": 2900,
  "currency": "AUD",
  "metadata": {
    "plan": "pro"
  }
}
crash Crash / fatal failure Send when the app crashes or captures a fatal failure.

Recommended fields: user_id or device_id, platform, build_version, metadata.error, metadata.stack, metadata.screen.

{
  "event_type": "crash",
  "device_id": "install_abc",
  "platform": "ios",
  "build_version": "1.4.2",
  "metadata": {
    "error": "Fatal exception",
    "screen": "Dashboard"
  }
}
error Handled error Send for handled errors worth tracking.

Recommended fields: user_id or device_id, platform, build_version, metadata.error, metadata.context.

{
  "event_type": "error",
  "user_id": "user_123",
  "platform": "web",
  "metadata": {
    "error": "Payment provider timeout",
    "context": "checkout"
  }
}
app_opened App opened Send when an app or site session starts.

Recommended fields: user_id if known, device_id, platform, build_version.

{
  "event_type": "app_opened",
  "device_id": "install_abc",
  "platform": "ios",
  "build_version": "1.4.2"
}
screen_viewed Screen / page view Send when a user views a screen or page.

Recommended fields: user_id or device_id, platform, metadata.screen or metadata.path.

{
  "event_type": "screen_viewed",
  "user_id": "user_123",
  "platform": "web",
  "metadata": {
    "path": "/dashboard"
  }
}
feature_used Feature usage Send when a meaningful feature is used.

Recommended fields: user_id or device_id, metadata.feature, metadata.action.

{
  "event_type": "feature_used",
  "user_id": "user_123",
  "metadata": {
    "feature": "export",
    "action": "csv_download"
  }
}
notification_sent Notification delivery Send when a notification is accepted by the sending provider.

Recommended fields: user_id, metadata.notification_id, metadata.channel, metadata.template.

{
  "event_type": "notification_sent",
  "user_id": "user_123",
  "metadata": {
    "notification_id": "notif_123",
    "channel": "push",
    "template": "weekly_summary"
  }
}
notification_opened Notification opened Send when a user opens a notification.

Recommended fields: user_id, device_id, metadata.notification_id, metadata.channel.

{
  "event_type": "notification_opened",
  "user_id": "user_123",
  "device_id": "install_abc",
  "metadata": {
    "notification_id": "notif_123",
    "channel": "push"
  }
}
health_check Ingest health check Send from monitors or jobs to prove ingest is alive.

Recommended fields: metadata.source, metadata.check.

{
  "event_type": "health_check",
  "metadata": {
    "source": "uptime_monitor",
    "check": "ingest"
  }
}

Events

2 endpoints

Submit one analytics event for the project attached to the ingest key.

Parameters
No parameters
Payload schema
{
  "type": "object",
  "description": "Analytics event payload. Use predefined event types such as signup, login, purchase, screen_viewed, feature_used, crash, error, notification_sent, and health_check. Purchase and subscription revenue events should include amount_cents and currency.",
  "required": [
    "event_type"
  ],
  "properties": {
    "event_type": {
      "type": "string",
      "enum": [
        "signup",
        "login",
        "active",
        "purchase",
        "subscription_started",
        "subscription_cancelled",
        "trial_started",
        "trial_converted",
        "crash",
        "error",
        "app_opened",
        "screen_viewed",
        "feature_used",
        "notification_sent",
        "notification_opened",
        "health_check"
      ],
      "example": "signup",
      "description": "Predefined event type or a project-defined custom event key."
    },
    "user_id": {
      "type": "string",
      "example": "user_123"
    },
    "external_user_id": {
      "type": "string",
      "example": "user_123"
    },
    "account_id": {
      "type": "string",
      "example": "team_123"
    },
    "external_account_id": {
      "type": "string",
      "example": "team_123"
    },
    "email": {
      "type": "string",
      "example": "founder@example.com"
    },
    "username": {
      "type": "string",
      "example": "founder"
    },
    "display_name": {
      "type": "string",
      "example": "Sam Founder"
    },
    "device_id": {
      "type": "string",
      "example": "install_abc"
    },
    "build_version": {
      "type": "string",
      "example": "1.4.2"
    },
    "platform": {
      "type": "string",
      "example": "ios"
    },
    "environment_id": {
      "type": "string",
      "format": "uuid",
      "example": "00000000-0000-0000-0000-000000000000"
    },
    "project_environment_id": {
      "type": "string",
      "format": "uuid",
      "example": "00000000-0000-0000-0000-000000000000"
    },
    "amount_cents": {
      "type": "integer",
      "format": "int64",
      "minimum": 0,
      "example": 1299,
      "description": "Non-negative integer amount in minor units. Required with currency for purchase, subscription_started, and trial_converted revenue reporting."
    },
    "currency": {
      "type": "string",
      "minLength": 3,
      "maxLength": 3,
      "example": "AUD",
      "description": "Three-letter ISO currency code. Required when amount_cents is present."
    },
    "occurred_at": {
      "type": "string",
      "format": "date-time"
    },
    "metadata": {
      "type": "object",
      "description": "Event-specific details such as plan, screen, feature, error, transaction_id, source, or check.",
      "example": {
        "plan": "free",
        "screen": "Dashboard",
        "transaction_id": "txn_123"
      }
    }
  }
}
Example request
curl -X POST "http://localhost:3000/v1/events" \
  -H "Authorization: Bearer ${INGEST_KEY}" \
  -H "Content-Type: application/json"
Responses
StatusDescription
201 Accepted
401 Missing, invalid, or discarded project API key
402 Monthly event limit reached
422 Invalid event payload
Response schema
{
  "type": "object",
  "properties": {
    "accepted": {
      "type": "boolean"
    },
    "accepted_count": {
      "type": "integer"
    }
  }
}
Response example
{
  "accepted": true,
  "accepted_count": 1
}
Try it
POST /v1/events

Submit multiple analytics events atomically. If any event is invalid, none are persisted.

Parameters
No parameters
Payload schema
{
  "type": "object",
  "required": [
    "events"
  ],
  "properties": {
    "events": {
      "type": "array",
      "items": {
        "type": "object",
        "description": "Analytics event payload. Use predefined event types such as signup, login, purchase, screen_viewed, feature_used, crash, error, notification_sent, and health_check. Purchase and subscription revenue events should include amount_cents and currency.",
        "required": [
          "event_type"
        ],
        "properties": {
          "event_type": {
            "type": "string",
            "enum": [
              "signup",
              "login",
              "active",
              "purchase",
              "subscription_started",
              "subscription_cancelled",
              "trial_started",
              "trial_converted",
              "crash",
              "error",
              "app_opened",
              "screen_viewed",
              "feature_used",
              "notification_sent",
              "notification_opened",
              "health_check"
            ],
            "example": "signup",
            "description": "Predefined event type or a project-defined custom event key."
          },
          "user_id": {
            "type": "string",
            "example": "user_123"
          },
          "external_user_id": {
            "type": "string",
            "example": "user_123"
          },
          "account_id": {
            "type": "string",
            "example": "team_123"
          },
          "external_account_id": {
            "type": "string",
            "example": "team_123"
          },
          "email": {
            "type": "string",
            "example": "founder@example.com"
          },
          "username": {
            "type": "string",
            "example": "founder"
          },
          "display_name": {
            "type": "string",
            "example": "Sam Founder"
          },
          "device_id": {
            "type": "string",
            "example": "install_abc"
          },
          "build_version": {
            "type": "string",
            "example": "1.4.2"
          },
          "platform": {
            "type": "string",
            "example": "ios"
          },
          "environment_id": {
            "type": "string",
            "format": "uuid",
            "example": "00000000-0000-0000-0000-000000000000"
          },
          "project_environment_id": {
            "type": "string",
            "format": "uuid",
            "example": "00000000-0000-0000-0000-000000000000"
          },
          "amount_cents": {
            "type": "integer",
            "format": "int64",
            "minimum": 0,
            "example": 1299,
            "description": "Non-negative integer amount in minor units. Required with currency for purchase, subscription_started, and trial_converted revenue reporting."
          },
          "currency": {
            "type": "string",
            "minLength": 3,
            "maxLength": 3,
            "example": "AUD",
            "description": "Three-letter ISO currency code. Required when amount_cents is present."
          },
          "occurred_at": {
            "type": "string",
            "format": "date-time"
          },
          "metadata": {
            "type": "object",
            "description": "Event-specific details such as plan, screen, feature, error, transaction_id, source, or check.",
            "example": {
              "plan": "free",
              "screen": "Dashboard",
              "transaction_id": "txn_123"
            }
          }
        }
      }
    }
  }
}
Example request
curl -X POST "http://localhost:3000/v1/events/batch" \
  -H "Authorization: Bearer ${INGEST_KEY}" \
  -H "Content-Type: application/json"
Responses
StatusDescription
201 Accepted
401 Missing, invalid, or discarded project API key
402 Monthly event limit reached
422 Invalid event payload
Response schema
{
  "type": "object",
  "properties": {
    "accepted": {
      "type": "boolean"
    },
    "accepted_count": {
      "type": "integer"
    }
  }
}
Response example
{
  "accepted": true,
  "accepted_count": 2
}
Try it
POST /v1/events/batch