> 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-documentation/api-guides/utilities/import-service/api-reference/analytics.md).

# Analytics

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

## Retrieving import statistics

> \<p align="left">\<img src="<https://res.cloudinary.com/saas-ag/image/upload/e\\_upscale/v1752824268/emporix/icons/preview\\_api1.png>" alt="" width="60" height="20">\</p>\
> \
> {% hint style="danger" %}\
> This functionality is in preview mode - some of the features may not be fully operational yet.\
> {% endhint %}\
> \
> 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."},"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 progress."}}},"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":"<p align=\"left\"><img src=\"https://res.cloudinary.com/saas-ag/image/upload/e_upscale/v1752824268/emporix/icons/preview_api1.png\" alt=\"\" width=\"60\" height=\"20\"></p>\n\n{% hint style=\"danger\" %}\nThis functionality is in preview mode - some of the features may not be fully operational yet.\n{% endhint %}\n\nRetrieves 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

> \<p align="left">\<img src="<https://res.cloudinary.com/saas-ag/image/upload/e\\_upscale/v1752824268/emporix/icons/preview\\_api1.png>" alt="" width="60" height="20">\</p>\
> \
> {% hint style="danger" %}\
> This functionality is in preview mode - some of the features may not be fully operational yet.\
> {% endhint %}\
> \
> 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":"<p align=\"left\"><img src=\"https://res.cloudinary.com/saas-ag/image/upload/e_upscale/v1752824268/emporix/icons/preview_api1.png\" alt=\"\" width=\"60\" height=\"20\"></p>\n\n{% hint style=\"danger\" %}\nThis functionality is in preview mode - some of the features may not be fully operational yet.\n{% endhint %}\n\nRetrieves 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

> \<p align="left">\<img src="<https://res.cloudinary.com/saas-ag/image/upload/e\\_upscale/v1752824268/emporix/icons/preview\\_api1.png>" alt="" width="60" height="20">\</p>\
> \
> {% hint style="danger" %}\
> This functionality is in preview mode - some of the features may not be fully operational yet.\
> {% endhint %}\
> \
> 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":"<p align=\"left\"><img src=\"https://res.cloudinary.com/saas-ag/image/upload/e_upscale/v1752824268/emporix/icons/preview_api1.png\" alt=\"\" width=\"60\" height=\"20\"></p>\n\n{% hint style=\"danger\" %}\nThis functionality is in preview mode - some of the features may not be fully operational yet.\n{% endhint %}\n\nRetrieves 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 dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://developer.emporix.io/api-documentation/api-guides/utilities/import-service/api-reference/analytics.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

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.
