ReferenceAvo Public APIInspector Issues

Inspector Issues

Read Inspector issues and the event shapes behind them over HTTP

Two GET endpoints: one returns a single Inspector issue, the other returns the event shapes (“variations”) behind it. Both take an issueId, which you’ll find in the Avo web app URL when you open an issue: https://www.avo.app/schemas/{workspaceId}/inspector/issues/ii/{issueId}.

The base URL for the Avo public API is https://api.avo.app.

Endpoints

Method and pathReturnsUse it when
GET /workspaces/:workspaceId/inspector/issues/v3/:issueIdA single issueYou need counts broken down per app version, or a window other than 24 hours.
GET /workspaces/:workspaceId/inspector/issues/:issueId/variationsThe event shapes behind an issue, as JSON or CSVYou want to see exactly what the event looked like when it triggered the issue.

:workspaceId is the ID of your workspace, from the URL of your Avo tab after /schemas/.

💡

Inspector data takes a few minutes to arrive, so an event sent moments ago won’t be in a response yet. To check an implementation as you ship it, use the Inspector Debugger.

Authentication

Both endpoints require an authorization header containing a Base64 encoded service account name and secret.

CodeBodyCondition
401{"message": "Authorization header missing"}No Authorization header.
401{"message": "Invalid authorization"}Bad secret, unknown service account, or a service account not registered in this workspace.

An issueId from another workspace returns 404, the same as an id that doesn’t exist.

Rate limits

Treat these endpoints as rate limited. Design for 1 request per second per service account, the limit soft-enforced across the rest of the Avo public API. Retry on 429 Too Many Requests with exponential backoff, and respect Retry-After if it’s there.

If you need a higher sustained rate, reach out.

Retrieving a single issue

GET https://api.avo.app/workspaces/:workspaceId/inspector/issues/v3/:issueId

Returns one issue, with its counts broken down per app version.

Query parameters

ParameterTypeDefaultAccepted values
timestring24hA number followed by h or d, such as 12h or 7d. Anything else falls back to 24h.

Response

FieldTypeNotes
issueIdstringIdentifies this issue on this source.
sharedIssueIdstringGroups the same problem across sources.
schemaIdstringYour workspace ID.
sourceIdstringThe source this issue was found on.
eventNamestringThe event name as sent.
propertyNamestring | nullnull for event-level issue types.
issueTypeobjectWhat kind of issue this is. See below.
oldestAppVersionstring
newestAppVersionstring
firstSeenstring (ISO 8601)
lastSeenstring (ISO 8601)
issueCountnumberOccurrences that violated, summed across versions.
eventCountnumberTotal occurrences of that event on that source, including the ones that were fine.
appVersionsobjectKeyed by version string, not an array. Each value is {"appVersion": string, "issueCount": number, "eventCount": number, "lastSeen": string | null}.
issueStatusobject{status, updatedAt: string | null, updatedBy: string | null}. See below.
regressionbooleanSee below.
branchIdsstring[][] when the issue isn’t linked to a branch.
⚠️

eventCount is not the number of events affected by the issue. It is the total volume of that event on that source, healthy occurrences included. issueCount is the part that violated. Report the ratio between them: issueCount: 1428 out of eventCount: 96204 is a 1.5% violation rate, not 96,204 broken events.

issueType

A type plus a payload key. For what each type means, see issue types in Inspector.

{ "type": "EventNotInTrackingPlan" }
{ "type": "UnexpectedEvent" }
{ "type": "MissingExpectedProperty", "missingExpectedProperty": { "eventId": "...", "propertyId": "...", "propertyName": "..." } }
{ "type": "PropertyTypeInconsistentWithTrackingPlan", "PropertyTypeInconsistentWithTrackingPlan": { "eventId": "..." , "propertyId": "...", "propertyName": "...", "expectedPropertyType": "...", "actualPropertyType": "..." } }
{ "type": "UnexpectedProperty", "unexpectedProperty": { "eventId": "...", "propertyName": "...", "propertyType": "..." } }
{ "type": "InconsistentType", "inconsistentType": { "propertyName": "...", "propertyTypes": ["string", "int"] } }

Payload keys are camelCase, except on PropertyTypeInconsistentWithTrackingPlan, where the key repeats the PascalCase type name.

issueStatus.status

{ "type": "Unresolved" }
{ "type": "Ignored",  "validateIn": { "type": "NextAppVersion", "appVersion": "8.15.0" } }
{ "type": "Resolved", "validateIn": { "type": "Never" } }

validateIn is one of {"type":"CurrentAppVersion","appVersion":string}, {"type":"NextAppVersion","appVersion":string}, {"type":"CustomAppVersion","appVersion":string}, {"type":"Date","date":ISO 8601} or {"type":"Never"}.

The status labelled Ignore in the Avo web app reads back as Ignored. An issue that never had a status set reads as Unresolved. See issue status for what each one means.

regression

true when an issue that was marked Resolved came back after the point it was meant to be fixed, which is the validateIn recorded when it was resolved. Inspector moves the issue back to Unresolved and sets the flag.

Setting the status again clears it, and an Ignored issue that comes back never sets it. It only means anything while the status is Unresolved.

Status codes

CodeBodyCondition
200The issue object
401See authenticationMissing or invalid credential.
404{"error": "Issue Not found"}No issue with this id in your workspace.
500{"error": "Internal Server Error"}

Example

$ curl -H "authorization: Basic <Base64 encoded token>" \
       -X GET "https://api.avo.app/workspaces/hAtPI0dEsq/inspector/issues/v3/2f1c9b8e4d7a05c3e6b1a94f8d2c70b5e93a17d4c8f0b62a5d1e7c3948fb0a26?time=7d"
{
  "issueId": "2f1c9b8e4d7a05c3e6b1a94f8d2c70b5e93a17d4c8f0b62a5d1e7c3948fb0a26",
  "sharedIssueId": "8b4d0f6a1c93e57204ab8d1f6e3c9057b24da8f1093c6e5b7d20a41fc8e93b56",
  "schemaId": "hAtPI0dEsq",
  "sourceId": "9Zq7YAo0R",
  "eventName": "Checkout Completed",
  "propertyName": "revenue",
  "issueType": {
    "type": "PropertyTypeInconsistentWithTrackingPlan",
    "PropertyTypeInconsistentWithTrackingPlan": {
      "eventId": "yT2rKpQ4Xa",
      "propertyId": "Bv8nLm1Zq0",
      "propertyName": "revenue",
      "expectedPropertyType": "float",
      "actualPropertyType": "string"
    }
  },
  "oldestAppVersion": "8.13.1",
  "newestAppVersion": "8.14.2",
  "firstSeen": "2026-08-11T09:42:18.000Z",
  "lastSeen": "2026-08-24T06:00:00.000Z",
  "issueCount": 9871,
  "eventCount": 644390,
  "appVersions": {
    "8.13.1": {
      "appVersion": "8.13.1",
      "issueCount": 7204,
      "eventCount": 402118,
      "lastSeen": "2026-08-23T21:00:00.000Z"
    },
    "8.14.2": {
      "appVersion": "8.14.2",
      "issueCount": 2667,
      "eventCount": 242272,
      "lastSeen": "2026-08-24T06:00:00.000Z"
    }
  },
  "issueStatus": {
    "status": { "type": "Unresolved" },
    "updatedAt": null,
    "updatedBy": null
  },
  "regression": false,
  "branchIds": []
}

Listing event variations

GET https://api.avo.app/workspaces/:workspaceId/inspector/issues/:issueId/variations

A variation is one observed shape of an event: a particular combination of property names and types, per app version, per source. This endpoint returns every shape the event was seen in over the last 24 hours, each with a causingIssue flag and a count, so you can compare a shape that triggered the issue against one that didn’t.

The window is always 24 hours and can’t be changed.

Query parameters

ParameterTypeDefaultAccepted values
formatstringjsoncsv returns CSV. Anything else returns JSON.
sourceIdstringNo filterOne source ID.
appVersionstringNo filterOne app version.

sourceId and appVersion take a single value each and match exactly, so ?sourceId=a,b finds nothing. Repeating a parameter is ignored.

⚠️

Without ?sourceId=, you get the event across every source in your workspace, not just the source the issue was reported on. Pass the issue’s own sourceId if that’s what you want.

The response is capped at 400 rows, and each shape produces one row per app version per source. variationsTruncated: true means you hit the cap and are only seeing part of the picture. Narrow the query with sourceId and appVersion, then check the flag again.

Response

{"variations": [...], "variationsTruncated": bool}, where each row has:

FieldTypeNotes
eventVariationKeystringIdentifies this shape on this source and app version.
causingIssuebooleanWhether this shape is one of the shapes causing the issue.
countnumberOccurrences in the window. An estimate on sampled sources.
eventNamestringThe event name as sent.
sourceIdstringThe Avo Source ID.
schemaIdstringYour workspace ID.
appVersionstring | null
minCreatedAtstring | nullISO 8601.
maxCreatedAtstring | nullISO 8601.
propertyNameSignaturestring[]The property names that were sent, sorted by name.
propertyTypeSignaturestring[]Their types, in the same order, so propertyTypeSignature[i] is the type of propertyNameSignature[i].

Rows also carry eventKey and sourceKey, which you can ignore.

⚠️

propertyNameSignature holds the names the SDK actually sent, not your tracking plan’s names. Line them up before comparing, or you’ll report differences that aren’t real. Names that look like data are redacted and come back as <Object redacted by Avo>, <ID string redacted by Avo> or <URL redacted by Avo>.

Nothing in the response says which event variant Inspector matched against.

CSV output

?format=csv returns the same rows arranged for comparison, as text/csv; charset=utf-8:

  1. # variationsTruncated: true or false.
  2. # Variations causing the issue, a header row, then the causing rows.
  3. # Variations not causing the issue, the same header row, then the rest.

Both sections appear even when empty. Columns are event_variation_key, causing_issue, count, event_name, source_id, app_version, min_created_at, max_created_at, then one column per property name, in the order the names first appear.

Each property cell holds that property’s type in that row, or is empty if the row doesn’t carry it. Every cell is quoted, including the header row; empty cells stay bare.

# variationsTruncated: false
# Variations causing the issue
"event_variation_key","causing_issue","count","event_name","source_id","app_version","min_created_at","max_created_at","currency","payment_method","revenue"
"5d2b81f0a37c94e618df05b2c7a3e9410fb86d24c503a1e79b0d4f6238ca7e15","true","1428","Checkout Completed","9Zq7YAo0R","8.14.2","2026-08-23T07:00:00.000Z","2026-08-24T06:00:00.000Z","string","string","string"
"b0f47ac125d3e896402fc7b13a5d90e648127cf3ab05d9e7261340bfc85a92d6","true","96","Checkout Completed","9Zq7YAo0R","8.13.1","2026-08-23T07:00:00.000Z","2026-08-24T05:00:00.000Z","string",,"string"
# Variations not causing the issue
"event_variation_key","causing_issue","count","event_name","source_id","app_version","min_created_at","max_created_at","currency","payment_method","revenue"
"e93c4a70b1d582f6047ae3c9128d5b0f76a2e841c30f9b57d6812ac4053e7fb9","false","94776","Checkout Completed","9Zq7YAo0R","8.14.2","2026-08-23T07:00:00.000Z","2026-08-24T06:00:00.000Z","string","string","float"

Status codes

CodeBodyCondition
200JSON or CSV
400{"error": "Invalid request"}Malformed request parameters.
401See authenticationMissing or invalid credential.
404{"error": "Issue not found"}No issue with this id in your workspace.
500{"error": "Internal Server Error"}

Example

$ curl -H "authorization: Basic <Base64 encoded token>" \
       -X GET "https://api.avo.app/workspaces/hAtPI0dEsq/inspector/issues/2f1c9b8e4d7a05c3e6b1a94f8d2c70b5e93a17d4c8f0b62a5d1e7c3948fb0a26/variations?sourceId=9Zq7YAo0R&appVersion=8.14.2"
{
  "variations": [
    {
      "eventVariationKey": "5d2b81f0a37c94e618df05b2c7a3e9410fb86d24c503a1e79b0d4f6238ca7e15",
      "causingIssue": true,
      "count": 1428,
      "eventName": "Checkout Completed",
      "sourceId": "9Zq7YAo0R",
      "schemaId": "hAtPI0dEsq",
      "appVersion": "8.14.2",
      "minCreatedAt": "2026-08-23T07:00:00.000Z",
      "maxCreatedAt": "2026-08-24T06:00:00.000Z",
      "eventKey": "a4e1c07b93d5f28601ab7c4e9d0f3b2586c1a97e4f0b3d8c25e6a1470bf9d3c8",
      "sourceKey": "hAtPI0dEsq-9Zq7YAo0R",
      "propertyNameSignature": ["currency", "payment_method", "revenue"],
      "propertyTypeSignature": ["string", "string", "string"]
    },
    {
      "eventVariationKey": "e93c4a70b1d582f6047ae3c9128d5b0f76a2e841c30f9b57d6812ac4053e7fb9",
      "causingIssue": false,
      "count": 94776,
      "eventName": "Checkout Completed",
      "sourceId": "9Zq7YAo0R",
      "schemaId": "hAtPI0dEsq",
      "appVersion": "8.14.2",
      "minCreatedAt": "2026-08-23T07:00:00.000Z",
      "maxCreatedAt": "2026-08-24T06:00:00.000Z",
      "eventKey": "a4e1c07b93d5f28601ab7c4e9d0f3b2586c1a97e4f0b3d8c25e6a1470bf9d3c8",
      "sourceKey": "hAtPI0dEsq-9Zq7YAo0R",
      "propertyNameSignature": ["currency", "payment_method", "revenue"],
      "propertyTypeSignature": ["string", "string", "float"]
    }
  ],
  "variationsTruncated": false
}

Both shapes carry the same property names. The only difference is the type of revenue: string on the shape causing the issue, float on the healthy one.

What’s next?