Daily Data Export API
The Daily Data Export API returns presigned download URLs for an AI Employee's Agent QA daily export and its manifest file. The export is generated nightly by Ema's internal workflow and covers all calls processed on the specified UTC date.
Use this API to pull evaluation results, scorecards, and insights into your own data warehouse, BI tool, or downstream analytics system.
| Header | Type | Required | Description |
Authorization | string | Yes | Bearer token (e.g., Bearer <token>) |
13.2 Get Daily Export Data
| Property | Value |
| URL | {base_url}/api/agent-qa/get_daily_data |
| HTTP Method | GET |
Query Parameters
| Field | Type | Required | Description |
persona_id | UUID | Yes | The unique identifier of the AI Employee (persona). Found in the URL of the Ema web app. |
date | string | Yes | UTC date to retrieve, in YYYY-MM-DD format. Must be strictly in the past (UTC). Today or future dates are rejected. |
Example Request
curl -X GET \
"{base_url}/api/agent-qa/get_daily_data?persona_id=<persona_id>&date=2025-06-01" \
-H "Authorization: Bearer <token>"
Response
A successful response (200 OK) returns a JSON object with the following fields:
| Field | Type | Description |
signed_url | string | Presigned download URL for the daily data export file (JSONL format). |
manifest_signed_url | string | Presigned download URL for the manifest JSON file. |
expires_in_seconds | integer | Number of seconds until the presigned URLs expire. Currently 3600 (1 hour). |
13.3 File Structure
The two presigned URLs point to files stored in Ema's internal cloud storage. One is a data file containing the actual records and results. The other is a manifest file describing the export partition. URLs expire within an hour; re-request if needed.
Data File (signed_url → agent_qa_export.jsonl)
The data file is JSONL (newline-delimited JSON). Each line is a self-contained JSON object representing one processed call record, corresponding to a single resource_id. The schema is defined by AgentQACallRecord.
| Field | Type | Required | Description |
resource_id | string | Yes | Unique external call identifier sent by the client (e.g., a Call ID). |
agent_id | string | Yes | Identifier of the human agent involved in the call. |
agent_email | string | No | Email of the agent. Used for permissioning. |
call_time | timestamp | Yes | ISO 8601 UTC timestamp of when the call occurred. |
persona_id | string | Yes | Identifier of the Agent QA AI Employee that processed this call. |
final_score | float | No | Calculated QA score on a 0–100 scale. Null if evaluation did not complete. |
sentiment | enum | No | Overall sentiment. One of: POSITIVE, NEGATIVE, NEUTRAL. |
resolution_status | enum | No | Whether the issue was resolved. One of: RESOLVED, NOT_RESOLVED. |
contact_reason_category | list[string] | No | Ordered list of categories explaining why the customer contacted support. |
call_driver_reason | string | No | Detailed free-text reason for the call. |
non_resolution_reason_category | list[string] | No | Reasons the call was not resolved. Only populated when resolution_status is NOT_RESOLVED. |
agent_talk_time | float | No | Duration (seconds) the agent was speaking. |
user_talk_time | float | No | Duration (seconds) the user was speaking. |
dead_air | float | No | Duration (seconds) of silence/dead air. |
total_audio_time | float | No | Total audio duration (seconds). |
case_id | string | No | Ticket/case ID associated with the call. |
agent_tenure | integer | No | Agent tenure in days. |
rule_results | list[object] | Yes | List of QA rule evaluation results. See below. |
insights | list[object] | Yes | List of behavioural insights. See below. |
Insight Attributes
| Field | Type | Description |
insight_type | enum | One of: STRENGTH, WEAKNESS, BUG, FEEDBACK, COMPETITOR. |
driver | string | High-level behavioural driver (e.g., "Information verification"). |
category | string | Sub-category within the driver (e.g., "Active listening"). |
justification | string | Explanation for why this insight was surfaced. |
verbatim | string | Transcript snippet supporting this insight. |
Rule Result Attributes
| Field | Type | Description |
rule_name | string | Name of the QA rule being evaluated. |
rule_category | string | Grouping of rules (e.g., "Customer experience", "Call management"). |
rule_weight | float | Weighting of this rule when calculating the final score. |
is_critical | bool | Whether this is a critical rule. If any critical rule fails, the recording receives a final score of 0. |
rule_mechanism | enum | Information source used. One of: TRANSCRIPT, TRANSCRIPT_WITH_KNOWLEDGE_BASE. |
result | enum | One of: PASS, FAIL, NOT_APPLICABLE. |
rationale | string | Explanation of why this rule passed, failed, or was not applicable. |
verbatims | list[string] | Transcript snippets supporting the evaluation result. |
Manifest File (manifest_signed_url → manifest.json)
The manifest file is a single JSON object describing the export partition. It is written alongside the data file after the nightly export completes successfully. The schema is defined by AgentQAExportManifest.
| Field | Type | Description |
tenant_id | string | The Ema tenant that owns this export. |
persona_id | string | The AI Employee persona for this export partition. |
date | string | Calendar date of the export partition in YYYY-MM-DD format. |
window_start | string | ISO 8601 UTC start of the export window (inclusive). |
window_end | string | ISO 8601 UTC end of the export window (exclusive). |
row_count | integer | Number of call records written to the data file. |
13.4 Error Codes
| HTTP Code | Reason |
400 Bad Request | Invalid persona_id format or other malformed request parameters. |
401 Unauthorized | Missing or invalid Authorization header. |
403 Forbidden | Credentials do not have edit-level permission to access the specified persona. |
404 Not Found | Persona does not exist, or no export is available for the requested date. |
422 Unprocessable Entity | date is not in YYYY-MM-DD format, or is today/future (must be a past UTC date). |
500 Internal Server Error | Unexpected server-side error. |
13.5 Rate Limits
A rate limit of 1 query per second is enforced on this API.
13.6 Sample Python
The snippet below downloads and parses agent_qa_export.jsonl from the presigned URL returned by the API.
import json
import urllib.request
# Step 1 — call the API to get the presigned URLs
req = urllib.request.Request(
"https://api.<region>.ema.co/api/agent-qa/get_daily_data"
"?persona_id=<persona_id>&date=2025-06-01",
headers={
"Authorization": "Bearer <token>",
"x-persona-id": "<persona_id>",
},
)
with urllib.request.urlopen(req) as resp:
urls = json.load(resp)
signed_url = urls["signed_url"]
manifest_signed_url = urls["manifest_signed_url"]
# Step 2 — download and parse the JSONL data file
records = []
with urllib.request.urlopen(signed_url) as resp:
for line in resp:
line = line.strip()
if line:
records.append(json.loads(line))
# Step 3 — work with the records
for record in records:
resource_id = record["resource_id"]
agent_id = record["agent_id"]
final_score = record.get("final_score")
sentiment = record.get("sentiment")
resolution = record.get("resolution_status")
rule_results = record["rule_results"]
insights = record["insights"]
print(f"{resource_id} | agent={agent_id} | score={final_score} | {sentiment} | {resolution}")
for rule in rule_results:
print(f" [{rule['result']}] {rule['rule_name']} (weight={rule['rule_weight']})")
for insight in insights:
print(f" [{insight['insight_type']}] {insight['driver']} — {insight['category']}")
# Step 4 — download and parse the manifest
with urllib.request.urlopen(manifest_signed_url) as resp:
manifest = json.load(resp)
print(f"Export date : {manifest['date']}")
print(f"Window : {manifest['window_start']} → {manifest['window_end']}")
print(f"Row count : {manifest['row_count']}")