Generating Biometric Reports with the Reporting API
Voice biometrics is a regulated, auditable activity. When a speaker is rejected, a fraud team needs to know what happened; when a regulator asks for evidence, you need the record of every enrollment and verification; and when an operations team tunes thresholds, it needs the outcomes to tune against. The Reporting API is how you get that data. It works the same way for every report: find out which reports exist, learn what fields and filters a report offers, then generate it with the filters, sort order, and page you want. This article walks through that workflow with a worked example.
Scope. This article covers the Voice Biometrics reports, served by the Biometric Reporting REST API. Usage reports for the speech services use a different API, described in Licensing & Usage Reporting. For the voice biometrics operations that these reports describe, see Voice Biometrics Concepts and Integration Flows. The headers every request needs are in API Access, Authentication and Required Headers.
The three calls
| Step | Call | Purpose |
|---|---|---|
| 1 | GET /Reporting/Biometrics/GetReportList | Lists the available reports. You can pass an optional reportCategory to list only one category. |
| 2 | GET /Reporting/Biometrics/GetReportDetails | Returns the fields a report contains and the filters it supports. Takes a required reportName. |
| 3 | POST /Reporting/Biometrics/GenerateReport | Generates the report, with the filters, sort order, and pagination in the JSON body. |
Before you start
Biometric reports are served by the Biometric Reporting API, which is exposed on its own hostname: reporting-bio-api followed by your hostnameSuffix, for example reporting-bio-api.example.com. If DNS is not configured, add a hosts file entry for it. See Setting Up a Deployment.
Every request needs the x-deployment-id, x-operator-id, and x-scopes headers, and can carry an x-correlation-id. For the Reporting API, the scope is REPO. If you give each call your own correlation ID, you can match the results to your own transactions.
Step 1: Find the report you need
Call GetReportList to see what is available:
| curl --request GET \ 'https://reporting-bio-api.example.com/Reporting/Biometrics/GetReportList' \ --header 'x-correlation-id: <unique-guid>' \ --header 'x-deployment-id: <your-deployment-id>' \ --header 'x-operator-id: <your-operator-id>' \ --header 'x-scopes: REPO' |
The response lists each report with its category:
| { "reportMetadatas": [ { "reportName": "BiometricOperation", "reportCategory": "Transaction" }, { "reportName": "BiometricSession", "reportCategory": "Transaction" }, { "reportName": "BiometricSessionDetails", "reportCategory": "Transaction" }, { "reportName": "AnalysisResultReport", "reportCategory": "Transaction" }, { "reportName": "EnrollmentStatus", "reportCategory": "Identity" } ] } |
Reports fall into categories such as Transaction (what happened) and Identity (the state of enrolled identities). Each entry can also carry a ReportDescription. The rest of this article uses BiometricOperation, which records the individual biometric operations such as enrollment and verification calls.
Step 2: Learn the fields and filters
Call GetReportDetails with the report name:
| curl --request GET \ 'https://reporting-bio-api.example.com/Reporting/Biometrics/GetReportDetails?reportName=BiometricOperation' \ --header 'x-correlation-id: <unique-guid>' \ --header 'x-deployment-id: <your-deployment-id>' \ --header 'x-operator-id: <your-operator-id>' \ --header 'x-scopes: REPO' |
The response has two parts. reportFieldMetadatas lists the fields in each row of the report, with their types. reportFieldFilterMetadatas lists the fields you can filter on and the attributes each filter takes.
| { "reportName": "BiometricOperation", "reportCategory": "Transaction", "reportFieldMetadatas": [ { "fieldName": "CorrelationId", "fieldType": "Uuid" }, { "fieldName": "OperationName", "fieldType": "Text" }, { "fieldName": "StartTime", "fieldType": "DateTime" }, { "fieldName": "EndTime", "fieldType": "DateTime" }, { "fieldName": "AssureIdentitySymbol", "fieldType": "Uuid" } ], "reportFieldFilterMetadatas": [ { "fieldFilterName": "StartTime", "fieldFilterAttributes": [ { "attributeName": "From", "attributeType": "DateTime", "validValues": "" }, { "attributeName": "To", "attributeType": "DateTime", "validValues": "" } ] }, { "fieldFilterName": "AssureIdentitySymbol", "fieldFilterAttributes": [ { "attributeName": "Value", "attributeType": "Uuid", "validValues": "" }, { "attributeName": "LogicalOperator", "attributeType": "Text", "validValues": "Equal,NotEqual" } ] } ] } |
The list is abridged here. Read it as follows: the report has the fields shown, and you can filter by a time range (From and To) on StartTime and EndTime, or by identity (Value with an optional LogicalOperator of Equal or NotEqual). In other words, you can ask for the biometric operations of one identity within a time range. The field name AssureIdentitySymbol is the identity symbol of the identity that the operation was performed for.
Step 3: Generate the report
Post a JSON body to GenerateReport. Only ReportName is required.
| Field | Purpose |
|---|---|
ReportName | The report to generate. Required. |
ReportFieldFilters | The filters to apply, as an object keyed by filter field name, with the attributes from GetReportDetails as values. |
ReportFieldSort | How to sort: the FieldName (required) and SortAscDesc (ascending or descending). |
ReportPagination | Which page to return: page (required, starting at 0) and perPage (the number of records per page). |
Example: one identity, newest first
| curl --request POST \ 'https://reporting-bio-api.example.com/Reporting/Biometrics/GenerateReport' \ --header 'content-type: application/json' \ --header 'x-correlation-id: <unique-guid>' \ --header 'x-deployment-id: <your-deployment-id>' \ --header 'x-operator-id: <your-operator-id>' \ --header 'x-scopes: REPO' \ --data '<body below>' |
| { "ReportName": "BiometricOperation", "ReportFieldFilters": { "AssureIdentitySymbol": { "value": "<identity-guid>" } }, "ReportFieldSort": { "FieldName": "StartTime", "SortAscDesc": "Desc" }, "ReportPagination": { "page": 0, "perPage": 100 } } |
The response contains the report name, a timestamp, how long generation took, the rows in ReportDataJson, and the sort and pagination that were applied:
| { "reportName": "BiometricOperation", "timeStamp": "2025-01-15T12:32:11.7176464Z", "generationTime": { "totalMilliseconds": 966.5419, "totalSeconds": 0.9665419 }, "reportDataJson": [ { "CorrelationId": "<guid>", "OperationName": "ProcessEnrollment", "StartTime": "2025-01-15T12:29:16.757643", "EndTime": "2025-01-15T12:29:17.023176", "AssureIdentitySymbol": "<identity-guid>", "ConfigurationSymbol": "<configuration-guid>", "OperatorId": 21, "OperationStatus": "Successful", "OperationErrorCode": 0, "OperationInfo": "Success" } ], "reportFieldSort": { "fieldName": "StartTime", "sortAscDesc": "Desc" }, "reportPagination": { "page": 0, "perPage": 100 } } |
The generation time is a duration object with many equivalent representations; the totalMilliseconds and totalSeconds values are the easiest to use. The rows are abridged.
Example: a time range and a different sort
Filters combine: every filter you list is applied. This body returns the operations of one identity that ended within a time window, sorted by status:
| { "ReportName": "BiometricOperation", "ReportFieldFilters": { "EndTime": { "From": "2025-01-15T10:16:41", "To": "2025-01-15T10:16:43" }, "AssureIdentitySymbol": { "value": "<identity-guid>" } }, "ReportFieldSort": { "FieldName": "OperationStatus", "SortAscDesc": "Asc" }, "ReportPagination": { "page": 0, "perPage": 100 } } |
The same approach works for every report: call GetReportDetails, read the filters it offers, and build the ReportFieldFilters object from them.
Filters that take several values
Some filters accept a list of values with a logical operator. The API reference shows a BiometricSession report filtered to sessions that ended in one of several statuses:
| { "ReportName": "BiometricSession", "ReportFieldFilters": { "StartTime": { "From": "2025-01-01", "To": "2026-01-01" }, "OperationStatus": { "Value": ["Successful", "InProgress"], "LogicOperator": "In" } }, "ReportFieldSort": { "FieldName": "EndTime", "SortAscDesc": "desc" }, "ReportPagination": { "page": 0, "perPage": 10 } } |
Always confirm the attribute names and valid values for the report you are using with GetReportDetails.
Paging through large results
Pagination splits a result into virtual pages. Set perPage to the number of rows you want in each page, request page 0 first, then increase page by one for each following request. The response repeats the pagination that was applied, so you can confirm the page size the platform used.
Using reports well
- Audit one person. Filter
BiometricOperationon the identity symbol and sort byStartTimeto see every enrollment and verification attempt, newest first. - Investigate a failure. Look at
OperationStatus,OperationErrorCode, andOperationInfofor the operation, and match it to your own logs with theCorrelationIdyou supplied. - Tune thresholds. Report on outcomes over a period, then review the thresholds with the Voice Biometrics Configuration Reference.
- Keep windows bounded. Use a time range and pagination, instead of requesting everything at once.
Errors
The API specification declares the HTTP status codes 400, 401, 404, and 500. Errors are returned as a JSON object with the fields Error, ErrorCode, HttpStatusCode, HttpErrorCode, Message, and MessageData. Read Message first.
Troubleshooting
| Symptom | Likely cause and fix |
|---|---|
| The request fails immediately | Send x-deployment-id, x-operator-id, and x-scopes: REPO, and make sure the IDs are valid GUIDs. See API Access, Authentication and Required Headers. |
| The host cannot be reached | Biometric reports use the reporting-bio-api hostname, which is separate from reporting-api. Check DNS or your hosts file. |
| The report name is not found | Use a name returned by GetReportList. Copy it from that response. |
| A filter does not behave as expected | Check the filter name and attribute names against GetReportDetails. They differ between reports. |
| The result is empty | The filters match no rows. Widen the time range, or check that the identity symbol is correct. |
| You only see part of the data | Results are paged. Request the next page by increasing page. |
| You cannot match a row to a transaction | Supply your own x-correlation-id on each biometric call, and match it to the CorrelationId field. |
