Owlmetry
API Reference

Apps

CRUD endpoints for managing apps — create, list, get, update, and delete apps within projects.

Create, list, get, update, and delete apps within projects.

Apps represent a specific platform build (e.g., iOS, Android, Web) within a project. Each app has its own client API key for SDK event ingestion.

GET /v1/apps

List all apps the caller has access to.

Auth required: Yes (apps:read permission or JWT)

ParameterTypeDescription
team_idstringOptional. Filter to a specific team.
// Response (200)
{
  "apps": [
    {
      "id": "uuid",
      "team_id": "uuid",
      "project_id": "uuid",
      "name": "iOS App",
      "platform": "apple",
      "bundle_id": "com.example.myapp",
      "client_secret": "owl_client_abc123...",
      "worldwide_rating_count": 1234,
      "worldwide_rating_count_delta": 8,
      "created_at": "2024-01-01T00:00:00.000Z"
    }
  ]
}

For Apple-platform apps, each row also carries the denormalized worldwide ratings rollup from the app_store_ratings_sync job: worldwide_average_rating, worldwide_rating_count, worldwide_current_version_rating, worldwide_current_version_rating_count, and ratings_synced_at. worldwide_rating_count_delta is the change in worldwide_rating_count since the previous daily snapshot — sum across countries of (latest − previous) rating_count, only counting countries with a prior snapshot. Tombstones contribute negatively; brand-new countries contribute 0. null when no app/country has prior data. Non-Apple platforms return null for all rating fields.

GET /v1/apps/:id

Get a single app by ID.

Auth required: Yes (apps:read permission or JWT)

// Response (200)
{
  "id": "uuid",
  "team_id": "uuid",
  "project_id": "uuid",
  "name": "iOS App",
  "platform": "apple",
  "bundle_id": "com.example.myapp",
  "client_secret": "owl_client_abc123...",
  "created_at": "2024-01-01T00:00:00.000Z"
}

POST /v1/apps

Create a new app within a project. A client API key is automatically generated and returned in the client_secret field.

Auth required: Yes (apps:write permission or JWT, admin role required)

// Request
{
  "name": "iOS App",
  "platform": "apple",
  "bundle_id": "com.example.myapp",
  "project_id": "uuid"
}

// Response (201)
{
  "id": "uuid",
  "team_id": "uuid",
  "project_id": "uuid",
  "name": "iOS App",
  "platform": "apple",
  "bundle_id": "com.example.myapp",
  "client_secret": "owl_client_full_key...",
  "created_at": "2024-01-01T00:00:00.000Z"
}

Field reference

FieldTypeRequiredDescription
namestringYesDisplay name for the app.
platformstringYesOne of: apple, android, web, backend.
bundle_idstringConditionalRequired for non-backend platforms. Immutable after creation.
project_idstringYesThe project this app belongs to. Team is derived from the project.

Platform notes

  • Backend apps do not require a bundle_id (it is set to null). The ingest route skips bundle_id validation for these apps.
  • bundle_id is immutable. It cannot be changed after app creation. To change a bundle_id, delete and recreate the app.

PATCH /v1/apps/:id

Update an app. Only the name field can be changed.

Auth required: Yes (apps:write permission or JWT, admin role required)

// Request
{
  "name": "Renamed App"
}

// Response (200)
{
  "id": "uuid",
  "team_id": "uuid",
  "project_id": "uuid",
  "name": "Renamed App",
  "platform": "apple",
  "bundle_id": "com.example.myapp",
  "client_secret": "owl_client_abc123...",
  "created_at": "2024-01-01T00:00:00.000Z"
}

DELETE /v1/apps/:id

Soft-delete an app and its associated API keys.

Auth required: Yes (JWT only, admin role required. Agent keys receive 403.)

// Response (200)
{
  "deleted": true
}

GET /v1/apps/:id/users

List users seen by a specific app, ordered by most recently seen.

Auth required: Yes (apps:read permission or JWT)

ParameterTypeDescription
searchstringFilter by user_id substring (case-insensitive).
is_anonymousstring"true" for anonymous users only, "false" for identified users only.
billing_statusstringFilter by billing tier (comma-separated). Values: free, trial, paid. Derived from RevenueCat integration properties.
cursorstringCursor from previous response.
limitnumberItems per page (1-200, default 50).
// Response (200)
{
  "users": [
    {
      "id": "uuid",
      "project_id": "uuid",
      "user_id": "owl_anon_abc123",
      "is_anonymous": true,
      "first_seen_at": "2024-01-01T00:00:00.000Z",
      "last_seen_at": "2024-01-15T10:30:00.000Z",
      "last_country_code": "US",
      "last_app_version": "1.2.0",
      "last_sdk_name": "owlmetry-swift",
      "last_sdk_version": "0.1.0",
      "claimed_from": null,
      "properties": {},
      "apps": [
        {
          "app_id": "uuid",
          "app_name": "iOS App",
          "first_seen_at": "2024-01-01T00:00:00.000Z",
          "last_seen_at": "2024-01-15T10:30:00.000Z"
        }
      ]
    }
  ],
  "cursor": "2024-01-14T08:00:00.000Z",
  "has_more": false
}

App users are project-scoped: the project_id identifies the project, and the apps array lists each app within that project that has seen this user. A user seen from multiple apps in the same project appears once, not once per app.

last_country_code is a 2-letter ISO country code derived server-side from the Cloudflare CF-IPCountry header on the most recent ingest request that carried one. It is never sent by SDKs, and is null until a request with a recognised country header arrives. Users seen only through backend apps stay null — the country header is dropped at ingest for backend apps because the request originates from a hosting datacenter rather than the end user. See Events › Country.

last_app_version is the app_version string from the most recent event ingested for this user. It mirrors the last_country_code pattern — updated on every batch from the incoming events — and is null until an event with a non-empty app_version arrives.

last_sdk_name and last_sdk_version are the SDK identifier and version from the most recent event ingested for this user (e.g. owlmetry-swift / 0.1.0). Auto-stamped by official SDKs and updated alongside last_app_version. Useful for spotting users on stale SDK versions.

GET /v1/app-users

List users across apps/projects/teams. Accepts the same filters as /v1/apps/:id/users plus team_id, project_id, app_id, since, and until for cross-app queries.

Auth required: Yes (apps:read permission or JWT)

Query parameters

ParameterTypeDescription
team_idstringFilter to a specific team.
project_idstringFilter to a specific project.
app_idstringFilter to users who have been seen from a specific app.
searchstringFilter by user_id substring (case-insensitive).
is_anonymousstring"true" for anonymous users only, "false" for identified users only.
billing_statusstringComma-separated billing tiers: free, trial, paid.
sincestringFilter by last_seen_at >= since. Relative or ISO 8601.
untilstringFilter by last_seen_at <= until. Relative or ISO 8601.
cursorstringCursor from previous response.
limitnumberItems per page (1-200, default 50).

Response shape is identical to /v1/apps/:id/users.

Ready to get started?

Connect your agent via MCP or CLI and start tracking.