{"openapi":"3.1.1","info":{"title":"Echo Route Partner Calls API","version":"1.0.0","license":{"name":"Proprietary","url":"https://echoroute.ai/terms"},"description":"Read-only call records for approved integration partners. This contract applies to organization-scoped partner keys; internal global staff keys are outside this contract and may return internal-only fields. The daily request limit is enforced and resets at UTC midnight; minute headers are informational approximations and do not promise minute-level enforcement. Keys without explicit PHI access receive the limited call shape. Full fields are returned only when the API key's phi_access flag is true."},"servers":[{"url":"https://api.echoroute.ai/functions/v1/external-api"}],"tags":[{"name":"Calls","description":"Read-only call records."}],"paths":{"/v1/calls":{"get":{"operationId":"listCalls","summary":"List calls","description":"Lists calls visible to the API key. Response fields depend on the key's PHI-access entitlement.","tags":["Calls"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"organization_id","in":"query","description":"Filter by organization UUID. Required when a non-global key covers more than one organization.","schema":{"type":"string","format":"uuid"}},{"name":"agent_id","in":"query","description":"Filter by agent UUID.","schema":{"type":"string","format":"uuid"}},{"name":"phone_number_id","in":"query","description":"Filter by Echo Route phone-number UUID.","schema":{"type":"string","format":"uuid"}},{"name":"date_from","in":"query","description":"Include calls starting on or after this date.","schema":{"type":"string","format":"date"}},{"name":"date_to","in":"query","description":"Include calls starting on or before this date.","schema":{"type":"string","format":"date"}},{"name":"category","in":"query","description":"Filter by call category.","schema":{"type":"string","enum":["hot_lead","info_request","spam","wrong_number"]}},{"name":"handling_type","in":"query","description":"Filter by how the call was handled.","schema":{"type":"string","enum":["ai_handled","transferred","voicemail"]}},{"name":"min_duration","in":"query","description":"Minimum call duration in seconds. Values greater than zero apply the filter.","schema":{"type":"integer"}},{"name":"sort","in":"query","description":"Field used to order results.","schema":{"type":"string","enum":["started_at","duration_seconds","category"],"default":"started_at"}},{"name":"sort_dir","in":"query","description":"Sort direction.","schema":{"type":"string","enum":["asc","desc"],"default":"desc"}},{"name":"limit","in":"query","description":"Maximum rows returned. Values above 100 are capped at 100.","schema":{"type":"integer","default":50,"maximum":100}},{"name":"offset","in":"query","description":"Zero-based result offset.","schema":{"type":"integer","default":0}}],"responses":{"200":{"description":"Successful response.","headers":{"X-RateLimit-Limit-Minute":{"$ref":"#/components/headers/RateLimitLimitMinute"},"X-RateLimit-Remaining-Minute":{"$ref":"#/components/headers/RateLimitRemainingMinute"},"X-RateLimit-Reset-Minute":{"$ref":"#/components/headers/RateLimitResetMinute"},"X-RateLimit-Limit-Day":{"$ref":"#/components/headers/RateLimitLimitDay"},"X-RateLimit-Remaining-Day":{"$ref":"#/components/headers/RateLimitRemainingDay"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CallListResponse"}}}},"400":{"description":"Invalid path or query parameter.","headers":{"X-RateLimit-Limit-Minute":{"$ref":"#/components/headers/RateLimitLimitMinute"},"X-RateLimit-Remaining-Minute":{"$ref":"#/components/headers/RateLimitRemainingMinute"},"X-RateLimit-Reset-Minute":{"$ref":"#/components/headers/RateLimitResetMinute"},"X-RateLimit-Limit-Day":{"$ref":"#/components/headers/RateLimitLimitDay"},"X-RateLimit-Remaining-Day":{"$ref":"#/components/headers/RateLimitRemainingDay"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Missing, invalid, inactive, revoked, or expired API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The key lacks calls-read permission or organization access.","headers":{"X-RateLimit-Limit-Minute":{"$ref":"#/components/headers/RateLimitLimitMinute"},"X-RateLimit-Remaining-Minute":{"$ref":"#/components/headers/RateLimitRemainingMinute"},"X-RateLimit-Reset-Minute":{"$ref":"#/components/headers/RateLimitResetMinute"},"X-RateLimit-Limit-Day":{"$ref":"#/components/headers/RateLimitLimitDay"},"X-RateLimit-Remaining-Day":{"$ref":"#/components/headers/RateLimitRemainingDay"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Daily request limit exceeded; retry after the UTC-day reset.","headers":{"X-RateLimit-Limit-Minute":{"$ref":"#/components/headers/RateLimitLimitMinute"},"X-RateLimit-Remaining-Minute":{"$ref":"#/components/headers/RateLimitRemainingMinute"},"X-RateLimit-Reset-Minute":{"$ref":"#/components/headers/RateLimitResetMinute"},"X-RateLimit-Limit-Day":{"$ref":"#/components/headers/RateLimitLimitDay"},"X-RateLimit-Remaining-Day":{"$ref":"#/components/headers/RateLimitRemainingDay"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server or data-access error.","headers":{"X-RateLimit-Limit-Minute":{"$ref":"#/components/headers/RateLimitLimitMinute"},"X-RateLimit-Remaining-Minute":{"$ref":"#/components/headers/RateLimitRemainingMinute"},"X-RateLimit-Reset-Minute":{"$ref":"#/components/headers/RateLimitResetMinute"},"X-RateLimit-Limit-Day":{"$ref":"#/components/headers/RateLimitLimitDay"},"X-RateLimit-Remaining-Day":{"$ref":"#/components/headers/RateLimitRemainingDay"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/calls/{id}":{"get":{"operationId":"getCall","summary":"Get a call","description":"Returns one call visible to the API key. Response fields depend on the key's PHI-access entitlement.","tags":["Calls"],"security":[{"ApiKeyAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Call UUID.","schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Successful response.","headers":{"X-RateLimit-Limit-Minute":{"$ref":"#/components/headers/RateLimitLimitMinute"},"X-RateLimit-Remaining-Minute":{"$ref":"#/components/headers/RateLimitRemainingMinute"},"X-RateLimit-Reset-Minute":{"$ref":"#/components/headers/RateLimitResetMinute"},"X-RateLimit-Limit-Day":{"$ref":"#/components/headers/RateLimitLimitDay"},"X-RateLimit-Remaining-Day":{"$ref":"#/components/headers/RateLimitRemainingDay"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CallDetailResponse"}}}},"400":{"description":"Invalid path or query parameter.","headers":{"X-RateLimit-Limit-Minute":{"$ref":"#/components/headers/RateLimitLimitMinute"},"X-RateLimit-Remaining-Minute":{"$ref":"#/components/headers/RateLimitRemainingMinute"},"X-RateLimit-Reset-Minute":{"$ref":"#/components/headers/RateLimitResetMinute"},"X-RateLimit-Limit-Day":{"$ref":"#/components/headers/RateLimitLimitDay"},"X-RateLimit-Remaining-Day":{"$ref":"#/components/headers/RateLimitRemainingDay"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Missing, invalid, inactive, revoked, or expired API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"The key lacks calls-read permission or organization access.","headers":{"X-RateLimit-Limit-Minute":{"$ref":"#/components/headers/RateLimitLimitMinute"},"X-RateLimit-Remaining-Minute":{"$ref":"#/components/headers/RateLimitRemainingMinute"},"X-RateLimit-Reset-Minute":{"$ref":"#/components/headers/RateLimitResetMinute"},"X-RateLimit-Limit-Day":{"$ref":"#/components/headers/RateLimitLimitDay"},"X-RateLimit-Remaining-Day":{"$ref":"#/components/headers/RateLimitRemainingDay"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Call not found.","headers":{"X-RateLimit-Limit-Minute":{"$ref":"#/components/headers/RateLimitLimitMinute"},"X-RateLimit-Remaining-Minute":{"$ref":"#/components/headers/RateLimitRemainingMinute"},"X-RateLimit-Reset-Minute":{"$ref":"#/components/headers/RateLimitResetMinute"},"X-RateLimit-Limit-Day":{"$ref":"#/components/headers/RateLimitLimitDay"},"X-RateLimit-Remaining-Day":{"$ref":"#/components/headers/RateLimitRemainingDay"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"description":"Daily request limit exceeded; retry after the UTC-day reset.","headers":{"X-RateLimit-Limit-Minute":{"$ref":"#/components/headers/RateLimitLimitMinute"},"X-RateLimit-Remaining-Minute":{"$ref":"#/components/headers/RateLimitRemainingMinute"},"X-RateLimit-Reset-Minute":{"$ref":"#/components/headers/RateLimitResetMinute"},"X-RateLimit-Limit-Day":{"$ref":"#/components/headers/RateLimitLimitDay"},"X-RateLimit-Remaining-Day":{"$ref":"#/components/headers/RateLimitRemainingDay"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"500":{"description":"Unexpected server or data-access error.","headers":{"X-RateLimit-Limit-Minute":{"$ref":"#/components/headers/RateLimitLimitMinute"},"X-RateLimit-Remaining-Minute":{"$ref":"#/components/headers/RateLimitRemainingMinute"},"X-RateLimit-Reset-Minute":{"$ref":"#/components/headers/RateLimitResetMinute"},"X-RateLimit-Limit-Day":{"$ref":"#/components/headers/RateLimitLimitDay"},"X-RateLimit-Remaining-Day":{"$ref":"#/components/headers/RateLimitRemainingDay"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}}},"components":{"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"X-API-Key","description":"Organization-scoped partner key issued by Echo Route. Internal global staff keys are outside this public contract."}},"headers":{"RateLimitLimitMinute":{"description":"Configured per-minute limit. This header is informational; minute-level enforcement is not guaranteed.","schema":{"type":"integer"}},"RateLimitRemainingMinute":{"description":"Approximate remaining requests in the current minute. Informational only.","schema":{"type":"integer"}},"RateLimitResetMinute":{"description":"Approximate Unix timestamp for the minute window reset. Informational only.","schema":{"type":"integer","format":"int64"}},"RateLimitLimitDay":{"description":"Enforced daily request limit for this API key.","schema":{"type":"integer"}},"RateLimitRemainingDay":{"description":"Remaining requests before the enforced daily limit is reached.","schema":{"type":"integer"}}},"schemas":{"LimitedCall":{"type":"object","additionalProperties":false,"properties":{"id":{"type":"string","format":"uuid"},"organization_id":{"type":"string","format":"uuid"},"organization_name":{"type":["string","null"]},"started_at":{"type":["string","null"],"format":"date-time"},"ended_at":{"type":["string","null"],"format":"date-time"},"duration_seconds":{"type":["integer","null"]},"outcome_type":{"type":["string","null"]},"category":{"type":["string","null"]},"business_phone_number":{"type":["string","null"]},"business_phone_label":{"type":["string","null"]},"has_recording":{"type":"boolean"}},"required":["id","organization_id","organization_name","started_at","ended_at","duration_seconds","outcome_type","category","business_phone_number","business_phone_label","has_recording"],"description":"Limited partner call fields. This schema is a technical exposure boundary and is not described as legally de-identified."},"FullCall":{"type":"object","additionalProperties":false,"properties":{"id":{"type":"string","format":"uuid"},"organization_id":{"type":"string","format":"uuid"},"organization_name":{"type":["string","null"]},"started_at":{"type":["string","null"],"format":"date-time"},"ended_at":{"type":["string","null"],"format":"date-time"},"duration_seconds":{"type":["integer","null"]},"caller_phone":{"type":["string","null"]},"caller_name":{"type":["string","null"]},"caller_email":{"type":["string","null"]},"caller_previous_call_count":{"type":["integer","null"]},"business_phone_number":{"type":["string","null"]},"business_phone_label":{"type":["string","null"]},"outcome_type":{"type":["string","null"]},"category":{"type":["string","null"]},"ai_summary":{"type":["string","null"]},"transcript":{"type":["string","null"]},"has_recording":{"type":"boolean"}},"required":["id","organization_id","organization_name","started_at","ended_at","duration_seconds","caller_phone","caller_name","caller_email","caller_previous_call_count","business_phone_number","business_phone_label","outcome_type","category","ai_summary","transcript","has_recording"],"description":"Full public call fields, returned only when the API key has explicit PHI access."},"PartnerCall":{"description":"The limited shape is returned by default. The full shape is returned only when the organization-scoped partner key's phi_access flag is true.","oneOf":[{"$ref":"#/components/schemas/LimitedCall"},{"$ref":"#/components/schemas/FullCall"}]},"CallListResponse":{"type":"object","additionalProperties":false,"properties":{"calls":{"type":"array","items":{"$ref":"#/components/schemas/PartnerCall"}},"total_count":{"type":"integer"},"limit":{"type":"integer"},"offset":{"type":"integer"}},"required":["calls","total_count","limit","offset"]},"CallDetailResponse":{"type":"object","additionalProperties":false,"properties":{"call":{"$ref":"#/components/schemas/PartnerCall"}},"required":["call"]},"ErrorDetail":{"type":"object","additionalProperties":true},"ErrorResponse":{"type":"object","additionalProperties":false,"properties":{"error":{"type":"object","additionalProperties":false,"properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"oneOf":[{"$ref":"#/components/schemas/ErrorDetail"},{"type":"null"}]}},"required":["code","message"]},"request_id":{"type":["string","null"]}},"required":["error","request_id"]}}}}