> For the complete documentation index, see [llms.txt](https://developer.emporix.io/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://developer.emporix.io/api-references/readme/api-reference-33/analytics.md).

# Analytics

Read aggregated import statistics, health, and the settings behind them.

## Retrieving import statistics

> Retrieves aggregated import metrics for a time window: headline counters and rates, a time series\
> bucketed by day, week, or month, per-stream health, the top failing streams, an error breakdown,\
> and how many streams were added or removed.\
> \
> To narrow the scope, use:\
> \- \`configId\` for one configuration\
> \- \`configIds\` for several configurations\
> \- \`streamId\` for one stream\
> \
> When none of them are given, the whole tenant is summarised. Use \`sections\` to fetch only the parts\
> you need — an unrequested section is returned as \`null\`.

```json
{"openapi":"3.0.0","info":{"title":"Import Service","version":"0.0.1"},"tags":[{"name":"Analytics","description":"Read aggregated import statistics, health, and the settings behind them."}],"servers":[{"url":"https://api.emporix.io"}],"security":[{"OAuth2":["importtool.import_trigger"]}],"components":{"securitySchemes":{"OAuth2":{"type":"oauth2","flows":{"clientCredentials":{"tokenUrl":"https://api.emporix.io/oauth/token","scopes":{"importtool.import_trigger":"Needed to trigger, schedule, monitor, and cancel import runs and to read configurations, streams, schedules, runs, and imported data."}}}}},"schemas":{"ImportStats":{"title":"Import statistics","type":"object","description":"Aggregated import metrics for the requested window and scope. A section that was not requested via the `sections` parameter is returned as `null`.","properties":{"summary":{"$ref":"#/components/schemas/ImportStatsSummary"},"series":{"type":"array","description":"Activity per time bucket, ordered oldest first and gap-free.","items":{"$ref":"#/components/schemas/ImportStatsPoint"}},"streamHealth":{"type":"array","description":"The health of each stream in scope.","items":{"$ref":"#/components/schemas/ImportStreamHealth"}},"topFailingStreams":{"type":"array","description":"The streams with the most errors during the time window, ordered by error count in descending order.","items":{"$ref":"#/components/schemas/ImportStatsNamedCount"}},"errorBreakdown":{"type":"array","description":"The most common errors in the window, grouped by stage and code.","items":{"$ref":"#/components/schemas/ImportStatsErrorBucket"}},"streamChanges":{"$ref":"#/components/schemas/ImportStreamChanges"},"sourceIssues":{"type":"array","description":"The source data does not match what the import process expects during the specified time window. These issues are not failures: the runs succeed, so they do not appear in pass/fail counters.","items":{"$ref":"#/components/schemas/ImportSourceIssue"}}}},"ImportStatsSummary":{"title":"Statistics summary","type":"object","description":"Headline totals for the window. Rates are fractions between 0 and 1.","properties":{"totalRuns":{"type":"integer","description":"The number of runs started in the window."},"succeeded":{"type":"integer","description":"The number of runs that finished successfully."},"partial":{"type":"integer","description":"The number of runs that finished with some records failing."},"failed":{"type":"integer","description":"The number of runs that failed."},"cancelled":{"type":"integer","description":"The number of runs that were cancelled."},"recordsRead":{"type":"integer","description":"The number of source records read."},"created":{"type":"integer","description":"The number of target objects created."},"updated":{"type":"integer","description":"The number of target objects updated."},"skipped":{"type":"integer","description":"The number of records skipped because nothing had changed."},"deleted":{"type":"integer","description":"The number of target objects deleted."},"failedRecords":{"type":"integer","description":"The number of records that could not be imported."},"successRate":{"type":"number","format":"double","description":"The share of runs that finished successfully."},"failureRate":{"type":"number","format":"double","description":"The share of records that failed."},"deletionRate":{"type":"number","format":"double","description":"The share of processed records that resulted in a deletion."},"skipRate":{"type":"number","format":"double","description":"The share of records skipped as unchanged."},"avgRunDurationSec":{"type":"number","format":"double","description":"The mean duration of a finished run, in seconds, measured from when the run began executing (`executionStartedAt`). Time spent in the queue is not counted."},"avgCreatedPerRun":{"type":"number","format":"double","description":"The mean number of objects created per run."},"avgUpdatedPerRun":{"type":"number","format":"double","description":"The mean number of objects updated per run."},"distinctEntities":{"type":"integer","description":"The number of distinct target objects currently held by the imports in scope."},"activeRuns":{"type":"integer","description":"The number of runs currently in the `QUEUED`, `RUNNING`, or `RESUMABLE` status. When you filter by `streamId`, queued runs are not counted, because they have not started any stream yet."}}},"ImportStatsPoint":{"title":"Statistics time bucket","type":"object","description":"One bucket of the time series, in UTC.","properties":{"bucket":{"type":"string","format":"date-time","description":"The start of the bucket."},"runs":{"type":"integer"},"succeeded":{"type":"integer"},"failed":{"type":"integer"},"recordsRead":{"type":"integer"},"created":{"type":"integer"},"updated":{"type":"integer"},"deleted":{"type":"integer"},"skipped":{"type":"integer"},"failedRecords":{"type":"integer"}}},"ImportStreamHealth":{"title":"Stream health","type":"object","description":"The health of one stream over the window, and the thresholds the verdict used.","properties":{"streamId":{"type":"string","format":"uuid"},"configId":{"type":"string","format":"uuid","description":"The configuration the stream belongs to."},"name":{"type":"string"},"targetType":{"type":"string","description":"The target type the stream writes to."},"lastRunStatus":{"type":"string","description":"The status of the stream's most recent run."},"lastRunAt":{"type":"string","format":"date-time"},"watermarkAgeSeconds":{"type":"integer","description":"The number of seconds since the stream last detected new data."},"failureRatio":{"type":"number","format":"double","description":"The share of the stream's records that failed in the window."},"health":{"type":"string","enum":["GREEN","AMBER","RED"],"description":"The stream's health verdict."},"thresholds":{"$ref":"#/components/schemas/ImportEffectiveThresholds"}}},"ImportEffectiveThresholds":{"title":"Effective thresholds","type":"object","description":"The threshold values the verdict was actually computed with, after resolving every level.","properties":{"failureRatioAmber":{"type":"number","format":"double"},"failureRatioRed":{"type":"number","format":"double"},"staleAmberHours":{"type":"integer"},"staleRedHours":{"type":"integer"},"failedRunIsRed":{"type":"boolean"},"customised":{"type":"boolean","description":"Whether any level overrides the built-in defaults."}}},"ImportStatsNamedCount":{"title":"Named count","type":"object","properties":{"streamId":{"type":"string","format":"uuid"},"name":{"type":"string"},"count":{"type":"integer"}}},"ImportStatsErrorBucket":{"title":"Error bucket","type":"object","description":"A count of errors sharing the same stage and code.","properties":{"stage":{"type":"string","description":"The pipeline stage the error occurred in."},"errorCode":{"type":"string"},"count":{"type":"integer"}}},"ImportStreamChanges":{"title":"Stream changes","type":"object","description":"The counts of streams added and removed during the time window.","properties":{"added":{"type":"integer"},"removed":{"type":"integer"},"trackingSince":{"type":"string","format":"date-time","description":"When stream tracking began for the tenant. Streams created before this point are not counted, so a window reaching further back is not comparable."},"series":{"type":"array","items":{"type":"object","properties":{"bucket":{"type":"string","format":"date-time"},"added":{"type":"integer"},"removed":{"type":"integer"}}}}}},"ImportSourceIssue":{"type":"object","properties":{"streamId":{"type":"string","format":"uuid","description":"The stream the problem was observed on."},"configId":{"type":"string","format":"uuid","description":"The configuration that owns the stream."},"name":{"type":"string","description":"The stream name."},"duplicateKeys":{"type":"integer","description":"Source rows that repeat a key already imported in the same run. The import keeps the last row of each repeated key and discards earlier rows."},"unresolvedParents":{"type":"integer","description":"Child records that were not imported because their parent could not be found."}}},"errorMessage":{"title":"Error","description":"Schema for specific API errors.","type":"object","properties":{"code":{"type":"integer","description":"HTTP status code.","minimum":100,"maximum":599},"status":{"description":"HTTP status.","type":"string"},"message":{"description":"Descriptive error message for debugging.","type":"string"},"details":{"description":"List of problems causing this error.","type":"array","items":{"title":"Error Detail","description":"Error details.","type":"string"}}},"required":["status","code","message"]}},"responses":{"Unauthorized_401":{"description":"Given request is unauthorized - the authorization token is invalid or has expired. Details will be provided in the response payload.","content":{"application/json":{"schema":{"type":"object","properties":{"fault":{"type":"object","properties":{"faultstring":{"type":"string"},"detail":{"type":"object","properties":{"errorcode":{"type":"string"}}}}}}}}}},"Forbidden_403":{"description":"Given authorization scopes are not sufficient and do not match scopes required by the endpoint.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/errorMessage"}}}},"InternalServiceError_500":{"description":"Internal Service Error occurred.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/errorMessage"}}}}}},"paths":{"/importtool/{tenant}/stats":{"get":{"summary":"Retrieving import statistics","description":"Retrieves aggregated import metrics for a time window: headline counters and rates, a time series\nbucketed by day, week, or month, per-stream health, the top failing streams, an error breakdown,\nand how many streams were added or removed.\n\nTo narrow the scope, use:\n- `configId` for one configuration\n- `configIds` for several configurations\n- `streamId` for one stream\n\nWhen none of them are given, the whole tenant is summarised. Use `sections` to fetch only the parts\nyou need — an unrequested section is returned as `null`.","operationId":"GET-importtool-stats","tags":["Analytics"],"parameters":[{"name":"configId","in":"query","required":false,"description":"Restricts the statistics to a single configuration.","schema":{"type":"string","format":"uuid"}},{"name":"configIds","in":"query","required":false,"description":"Restricts the statistics to several configurations, as a comma-separated list of identifiers. Ignored when `configId` is given.","schema":{"type":"string"}},{"name":"streamId","in":"query","required":false,"description":"Restricts the statistics to a single stream. Takes precedence over the configuration filters.","schema":{"type":"string","format":"uuid"}},{"name":"from","in":"query","required":false,"description":"The start of the window, as an ISO-8601 instant. When omitted, defaults to 30 days ago.","schema":{"type":"string","format":"date-time"}},{"name":"to","in":"query","required":false,"description":"The end of the window, as an ISO-8601 instant. Exclusive. When omitted, defaults to now.","schema":{"type":"string","format":"date-time"}},{"name":"granularity","in":"query","required":false,"description":"The bucket size of the returned time series.","schema":{"type":"string","enum":["DAY","WEEK","MONTH"],"default":"DAY"}},{"name":"sections","in":"query","required":false,"description":"A comma-separated list of the sections to compute: `TOTALS`, `STREAMS`, `ERRORS`, `CHANGES`. When omitted, defaults to all sections. A section not requested through the `sections` parameter is returned as `null`.","schema":{"type":"string"}}],"responses":{"200":{"description":"The request was successful. The statistics are returned.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ImportStats"}}}},"400":{"description":"The window or a filter value is invalid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/errorMessage"}}}},"401":{"$ref":"#/components/responses/Unauthorized_401"},"403":{"$ref":"#/components/responses/Forbidden_403"},"500":{"$ref":"#/components/responses/InternalServiceError_500"}}}}}}
```

## Retrieving job groups

> Retrieves the tenant's job groups. A job group is a named set of import configurations, used to\
> scope the analytics to the imports that matter to you. Groups are stored per tenant and shared by\
> everyone working on it.\
> \
> Creating, changing, and deleting groups requires the \`importtool.import\_manage\` scope.

```json
{"openapi":"3.0.0","info":{"title":"Import Service","version":"0.0.1"},"tags":[{"name":"Analytics","description":"Read aggregated import statistics, health, and the settings behind them."}],"servers":[{"url":"https://api.emporix.io"}],"security":[{"OAuth2":["importtool.import_trigger"]}],"components":{"securitySchemes":{"OAuth2":{"type":"oauth2","flows":{"clientCredentials":{"tokenUrl":"https://api.emporix.io/oauth/token","scopes":{"importtool.import_trigger":"Needed to trigger, schedule, monitor, and cancel import runs and to read configurations, streams, schedules, runs, and imported data."}}}}},"schemas":{"JobGroup":{"title":"Job group","type":"object","description":"A named set of import configurations, used to scope the import statistics.","properties":{"id":{"type":"string","format":"uuid","description":"The job group identifier."},"name":{"type":"string","description":"The job group name. Unique within the tenant."},"configIds":{"type":"array","description":"The configurations in the group.","items":{"type":"string","format":"uuid"}},"createdAt":{"type":"string","format":"date-time","description":"When the group was created."},"updatedAt":{"type":"string","format":"date-time","description":"When the group was last changed."}}},"errorMessage":{"title":"Error","description":"Schema for specific API errors.","type":"object","properties":{"code":{"type":"integer","description":"HTTP status code.","minimum":100,"maximum":599},"status":{"description":"HTTP status.","type":"string"},"message":{"description":"Descriptive error message for debugging.","type":"string"},"details":{"description":"List of problems causing this error.","type":"array","items":{"title":"Error Detail","description":"Error details.","type":"string"}}},"required":["status","code","message"]}},"responses":{"Unauthorized_401":{"description":"Given request is unauthorized - the authorization token is invalid or has expired. Details will be provided in the response payload.","content":{"application/json":{"schema":{"type":"object","properties":{"fault":{"type":"object","properties":{"faultstring":{"type":"string"},"detail":{"type":"object","properties":{"errorcode":{"type":"string"}}}}}}}}}},"Forbidden_403":{"description":"Given authorization scopes are not sufficient and do not match scopes required by the endpoint.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/errorMessage"}}}},"InternalServiceError_500":{"description":"Internal Service Error occurred.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/errorMessage"}}}}}},"paths":{"/importtool/{tenant}/dashboard/job-groups":{"get":{"summary":"Retrieving job groups","description":"Retrieves the tenant's job groups. A job group is a named set of import configurations, used to\nscope the analytics to the imports that matter to you. Groups are stored per tenant and shared by\neveryone working on it.\n\nCreating, changing, and deleting groups requires the `importtool.import_manage` scope.","operationId":"GET-importtool-list-job-groups","tags":["Analytics"],"responses":{"200":{"description":"The request was successful. The job groups are returned.","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/JobGroup"}}}}},"401":{"$ref":"#/components/responses/Unauthorized_401"},"403":{"$ref":"#/components/responses/Forbidden_403"},"500":{"$ref":"#/components/responses/InternalServiceError_500"}}}}}}
```

## Retrieving the tenant's health thresholds

> Retrieves the tenant-wide thresholds that decide whether a stream is reported as \`GREEN\`, \`AMBER\`,\
> or \`RED\` in the import statistics, together with the built-in defaults they fall back to.\
> \
> Thresholds resolve field by field, from the most specific level to the least specific: stream,\
> configuration, tenant, then the built-in default. An unset field falls back to the value at the\
> next less-specific level.\
> \
> Changing the tenant-wide thresholds requires the \`importtool.import\_manage\` scope.

```json
{"openapi":"3.0.0","info":{"title":"Import Service","version":"0.0.1"},"tags":[{"name":"Analytics","description":"Read aggregated import statistics, health, and the settings behind them."}],"servers":[{"url":"https://api.emporix.io"}],"security":[{"OAuth2":["importtool.import_trigger"]}],"components":{"securitySchemes":{"OAuth2":{"type":"oauth2","flows":{"clientCredentials":{"tokenUrl":"https://api.emporix.io/oauth/token","scopes":{"importtool.import_trigger":"Needed to trigger, schedule, monitor, and cancel import runs and to read configurations, streams, schedules, runs, and imported data."}}}}},"schemas":{"HealthSettings":{"title":"Tenant health thresholds","type":"object","description":"The tenant-wide thresholds, together with the built-in defaults they fall back to.","properties":{"thresholds":{"$ref":"#/components/schemas/HealthThresholds"},"builtInDefaults":{"$ref":"#/components/schemas/HealthThresholds"}}},"HealthThresholds":{"title":"Health thresholds","type":"object","description":"The limits that decide whether a stream is reported as `GREEN`, `AMBER`, or `RED`. Each field is optional and resolves independently. An unset field falls back through the hierarchy, from the most specific level to the least specific: stream, configuration, tenant, then the built-in default.","properties":{"failureRatioAmber":{"type":"number","format":"double","description":"The share of failed records (0..1) at or above which the stream is `AMBER`."},"failureRatioRed":{"type":"number","format":"double","description":"The share of failed records (0..1) at or above which the stream is `RED`."},"staleAmberHours":{"type":"integer","description":"The age threshold, in hours, for the stream's newest data. The stream is `AMBER` when the data's age reaches or exceeds this threshold."},"staleRedHours":{"type":"integer","description":"The age threshold, in hours, for the stream's newest data. The stream is `RED` when the data's age reaches or exceeds this threshold."},"failedRunIsRed":{"type":"boolean","description":"Whether a failed most-recent run makes the stream `RED` regardless of the other limits."}}},"errorMessage":{"title":"Error","description":"Schema for specific API errors.","type":"object","properties":{"code":{"type":"integer","description":"HTTP status code.","minimum":100,"maximum":599},"status":{"description":"HTTP status.","type":"string"},"message":{"description":"Descriptive error message for debugging.","type":"string"},"details":{"description":"List of problems causing this error.","type":"array","items":{"title":"Error Detail","description":"Error details.","type":"string"}}},"required":["status","code","message"]}},"responses":{"Unauthorized_401":{"description":"Given request is unauthorized - the authorization token is invalid or has expired. Details will be provided in the response payload.","content":{"application/json":{"schema":{"type":"object","properties":{"fault":{"type":"object","properties":{"faultstring":{"type":"string"},"detail":{"type":"object","properties":{"errorcode":{"type":"string"}}}}}}}}}},"Forbidden_403":{"description":"Given authorization scopes are not sufficient and do not match scopes required by the endpoint.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/errorMessage"}}}},"InternalServiceError_500":{"description":"Internal Service Error occurred.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/errorMessage"}}}}}},"paths":{"/importtool/{tenant}/settings/health-thresholds":{"get":{"summary":"Retrieving the tenant's health thresholds","description":"Retrieves the tenant-wide thresholds that decide whether a stream is reported as `GREEN`, `AMBER`,\nor `RED` in the import statistics, together with the built-in defaults they fall back to.\n\nThresholds resolve field by field, from the most specific level to the least specific: stream,\nconfiguration, tenant, then the built-in default. An unset field falls back to the value at the\nnext less-specific level.\n\nChanging the tenant-wide thresholds requires the `importtool.import_manage` scope.","operationId":"GET-importtool-health-thresholds","tags":["Analytics"],"responses":{"200":{"description":"The request was successful. The thresholds are returned.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HealthSettings"}}}},"401":{"$ref":"#/components/responses/Unauthorized_401"},"403":{"$ref":"#/components/responses/Forbidden_403"},"500":{"$ref":"#/components/responses/InternalServiceError_500"}}}}}}
```


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://developer.emporix.io/api-references/readme/api-reference-33/analytics.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
