Getting Started with the Viv API
Welcome to the Viv Public API. This guide will help you integrate with Viv's home care management platform to manage clients, caregivers, visits, EVV, and more.
Base URL: https://{your-api-host}/api/v2/public
Your API host is specific to your agency and will be provided with your API credentials.
Quick Start
1. Get Your API Key
Contact your Viv account manager or email [email protected] to request API access.
You'll receive:
- API Key — Your unique authentication credential
- API Host — Your agency-specific API endpoint
- Scopes — Which resources your key can access (clients, visits, etc.)
- Location Access — Which geographic locations you can access
2. Make Your First Request
Test your credentials by fetching your available locations:
curl -X GET "https://{your-api-host}/api/v2/public/config/locations" \
-H "x-api-key: YOUR_API_KEY"Note: Your API key must have the
config:readscope to access configuration endpoints.
{
"data": [
{
"id": "5633a2df99b4cd291e7044d8",
"name": "Toronto",
"timezone": "America/Toronto",
"state": "Ontario",
"country": "Canada"
}
]
}If you see your authorized locations, you're ready to go!
3. Fetch Some Clients
curl -X GET "https://{your-api-host}/api/v2/public/clients?limit=5" \
-H "x-api-key: YOUR_API_KEY"{
"data": [
{
"id": "507f1f77bcf86cd799439011",
"firstName": "John",
"lastName": "Smith",
"status": "active",
"locationId": "5633a2df99b4cd291e7044d8"
}
],
"pagination": {
"total": 150,
"limit": 5,
"offset": 0,
"hasMore": true
}
}Authentication
All API requests require an API key passed in the x-api-key header:
curl -X GET "https://{your-api-host}/api/v2/public/clients" \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json"
Security Best Practices:
- Never expose your API key in client-side code or public repositories
- Store keys securely using environment variables or secret management
- Use server-to-server calls — never call the API directly from browsers
- Contact us immediately if your key is compromised for rotation
API Scopes
Your API key is granted specific scopes that control access. Common scopes include:
| Scope | Access |
|---|---|
clients | Read/write client records |
clients:read | Read-only client access |
caregivers | Read/write caregiver records |
visits | Read/write visit/booking data |
evv | Read/write EVV check-in/out data |
config:read | Read configuration data (locations, attributes, etc.) |
webhooks | Manage webhook subscriptions |
report-builder:read | Execute custom reports |
Attempting to access resources without the required scope returns a 403 Forbidden error.
Core Concepts
Locations (Service Areas)
Viv organizes data by geographic location (office/service area). Your API key grants access to specific locations. All entities (clients, caregivers, visits) belong to a location via the locationId field.
GET /config/locations{
"data": [
{
"id": "5633a2df99b4cd291e7044d8",
"name": "Toronto",
"timezone": "America/Toronto",
"state": "Ontario",
"country": "Canada"
}
]
}Configurable Attributes
Many fields use agency-configurable attributes. These include certifications, languages, case types, cancellation reasons, and more.
GET /config/attributes?type=certification{
"data": [
{ "id": "5633a2df99b4cd291e704530", "text": "PSW", "type": "certification" },
{ "id": "5633a2df99b4cd291e704531", "text": "RN", "type": "certification" }
]
}Without the type parameter, you get a list of available attribute types:
{
"data": [
{ "type": "certification", "label": "Certification" },
{ "type": "language", "label": "Language" },
{ "type": "caseType", "label": "Case Type" }
]
}Entity IDs
All entities use MongoDB ObjectIDs — 24-character hexadecimal strings:
507f1f77bcf86cd799439011
Pagination
All list endpoints return paginated results:
GET /clients?limit=20&offset=40| Parameter | Type | Default | Max | Description |
|---|---|---|---|---|
limit | integer | 20 | 100 | Results per page |
offset | integer | 0 | 10000 | Number of results to skip |
Response format:
{
"data": [...],
"pagination": {
"total": 1250,
"limit": 20,
"offset": 40,
"hasMore": true
}
}Filtering
Most list endpoints support filtering by common fields:
# Clients by status
GET /clients?status=active
# Visits by date range
GET /visits?startDate=2024-12-01&endDate=2024-12-31
# Visits by client
GET /visits?clientId=507f1f77bcf86cd799439011
# Combined filters
GET /visits?clientId=507f1f77bcf86cd799439011&startDate=2024-12-01&hasCheckIn=trueSyncing Data
Use updatedSince to fetch only records modified after a specific timestamp:
GET /clients?updatedSince=2024-12-01T00:00:00ZRecommended sync pattern:
- Initial sync — Paginate through all records, store the sync timestamp
- Incremental syncs — Use
updatedSincewith your stored timestamp - Update timestamp — After each successful sync, update your stored timestamp
import requests
from datetime import datetime
def incremental_sync(last_sync_time):
"""Fetch only records modified since last sync"""
response = requests.get(
f"{BASE_URL}/clients",
headers={"x-api-key": API_KEY},
params={"updatedSince": last_sync_time, "limit": 100}
)
new_sync_time = datetime.utcnow().isoformat() + "Z"
return response.json(), new_sync_timeNote: Deletion tracking is not currently supported. For applications requiring deletion awareness, perform periodic full syncs to reconcile your local data.
Rate Limits
| Endpoint Type | Default Limit |
|---|---|
| Read (GET) | 10 requests/second |
| Write (POST/PUT/DELETE) | 1 request/second |
Custom rate limits can be configured per API key. When rate limited, you'll receive:
HTTP 429 Too Many Requests
{
"error": {
"code": "RATE_LIMITED",
"message": "Too many requests. Please retry after 1 second.",
"retryAfter": 1
}
}Best practices:
- Implement exponential backoff on 429 responses
- Use the
retryAftervalue to schedule retries - Batch operations where possible
Error Handling
All errors follow a consistent format:
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Invalid request parameters",
"details": {
"field": "email",
"reason": "Invalid email format"
}
}
}HTTP Status Codes
| Code | Meaning |
|---|---|
200 | Success |
201 | Created |
204 | Deleted (no content) |
400 | Bad request / Validation error |
401 | Invalid or missing API key |
403 | Forbidden (insufficient scope or locked resource) |
404 | Resource not found |
409 | Conflict (e.g., duplicate entry, invalid state transition) |
429 | Rate limited |
500 | Server error |
Common Error Codes
| Code | Description |
|---|---|
VALIDATION_ERROR | Request body failed validation |
NOT_FOUND | Resource doesn't exist |
UNAUTHORIZED | API key invalid or missing |
INSUFFICIENT_PERMISSIONS | API key lacks required scope |
LOCKED | Resource is locked and cannot be modified |
RATE_LIMITED | Too many requests |
ALREADY_EXISTS | Duplicate resource |
Locked Resources
Certain resources become locked and cannot be modified:
- Visits — Locked when included in finalized payroll, submitted for billing, or sent to EVV aggregator
- EVV Records — Locked after submission to state aggregator
Attempting to modify a locked resource returns:
HTTP 403 Forbidden
{
"error": {
"code": "LOCKED",
"message": "Visit is locked for payroll and cannot be modified",
"details": {
"lockReason": "payroll",
"lockedAt": "2024-12-15T00:00:00Z"
}
}
}Check the isLocked field on visits to determine lock status before attempting updates.
Webhooks
Subscribe to real-time events instead of polling:
POST /webhooks{
"name": "My Integration",
"url": "https://your-app.com/webhooks/viv",
"events": ["client.created", "client.updated", "visit.completed"],
"secret": "your-webhook-secret"
}Popular event types:
| Category | Events |
|---|---|
| Clients | client.created, client.updated, client.status_changed |
| Caregivers | caregiver.created, caregiver.activated, caregiver.terminated |
| Visits | visit.created, visit.updated, visit.completed, visit.cancelled |
| EVV | evv.check_in, evv.check_out |
| Authorizations | authorization.created, authorization.updated |
Webhook payloads include the full resource data and are signed with your secret for verification.
Code Examples
Python: Paginated Client Sync
import requests
API_KEY = "your_api_key"
BASE_URL = "https://your-api-host/api/v2/public"
headers = {"x-api-key": API_KEY}
def fetch_all_clients():
"""Fetch all clients with automatic pagination"""
clients = []
offset = 0
while True:
response = requests.get(
f"{BASE_URL}/clients",
headers=headers,
params={"limit": 100, "offset": offset}
)
response.raise_for_status()
data = response.json()
clients.extend(data["data"])
if not data["pagination"]["hasMore"]:
break
offset += 100
return clientsJavaScript: Create a Visit
const API_KEY = 'your_api_key';
const BASE_URL = 'https://your-api-host/api/v2/public';
async function createVisit(clientId, caregiverId, startTime, endTime) {
const response = await fetch(`${BASE_URL}/visits`, {
method: 'POST',
headers: {
'x-api-key': API_KEY,
'Content-Type': 'application/json',
},
body: JSON.stringify({
clientId,
caregiverId,
scheduledStartTime: startTime,
scheduledEndTime: endTime,
}),
});
if (!response.ok) {
const error = await response.json();
throw new Error(error.error.message);
}
return response.json();
}cURL: Check EVV Status
# Get EVV data for a visit
curl -X GET "https://{your-api-host}/api/v2/public/visits/{visitId}/evv" \
-H "x-api-key: YOUR_API_KEY"
# Record check-in
curl -X POST "https://{your-api-host}/api/v2/public/visits/{visitId}/evv/checkin" \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"time": "2024-12-15T09:00:00Z",
"latitude": 43.6532,
"longitude": -79.3832,
"method": "gps"
}'Common Integration Patterns
Mobile App Integration
- Server-side proxy — Never expose API keys in mobile apps. Route requests through your backend.
- Offline sync — Use
updatedSincefor efficient data synchronization - EVV capture — Use the EVV endpoints to record GPS-verified check-in/out
Billing System Integration
- Fetch visits — Get completed visits with
GET /visits?hasCheckOut=true - Get authorization — Verify hours against
GET /authorizations/{id} - Export data — Use pagination to export billing-ready data
HR System Integration
- Sync caregivers — Fetch active caregivers with
GET /caregivers?status=active - Track certifications — Monitor expiring credentials
- Webhook alerts — Subscribe to
caregiver.terminatedevents
Support
Technical Support: [email protected]
Our support team monitors requests during business hours and will respond as quickly as possible.
Planned Enhancements
The following features are under consideration for future API versions:
- EVV reason codes — Signal when check-in/out requires an early/late or GPS out-of-fence reason
- Deletion tracking — Track deleted records for offline sync scenarios
- Visit rates — Full rate information in visit API responses
Have a feature request? Contact [email protected].
What's Next?
- API Reference — Explore all endpoints
- Clients API — Manage client records
- Visits API — Schedule and track visits
- EVV API — Electronic Visit Verification
- Webhooks — Set up real-time notifications
Updated about 1 month ago