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

StepCallPurpose
1GET /Reporting/Biometrics/GetReportListLists the available reports. You can pass an optional reportCategory to list only one category.
2GET /Reporting/Biometrics/GetReportDetailsReturns the fields a report contains and the filters it supports. Takes a required reportName.
3POST /Reporting/Biometrics/GenerateReportGenerates 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.

FieldPurpose
ReportNameThe report to generate. Required.
ReportFieldFiltersThe filters to apply, as an object keyed by filter field name, with the attributes from GetReportDetails as values.
ReportFieldSortHow to sort: the FieldName (required) and SortAscDesc (ascending or descending).
ReportPaginationWhich 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 BiometricOperation on the identity symbol and sort by StartTime to see every enrollment and verification attempt, newest first.
  • Investigate a failure. Look at OperationStatus, OperationErrorCode, and OperationInfo for the operation, and match it to your own logs with the CorrelationId you 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

SymptomLikely cause and fix
The request fails immediatelySend 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 reachedBiometric reports use the reporting-bio-api hostname, which is separate from reporting-api. Check DNS or your hosts file.
The report name is not foundUse a name returned by GetReportList. Copy it from that response.
A filter does not behave as expectedCheck the filter name and attribute names against GetReportDetails. They differ between reports.
The result is emptyThe filters match no rows. Widen the time range, or check that the identity symbol is correct.
You only see part of the dataResults are paged. Request the next page by increasing page.
You cannot match a row to a transactionSupply your own x-correlation-id on each biometric call, and match it to the CorrelationId field.

Related Articles


Was this article helpful?