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:read scope 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:

ScopeAccess
clientsRead/write client records
clients:readRead-only client access
caregiversRead/write caregiver records
visitsRead/write visit/booking data
evvRead/write EVV check-in/out data
config:readRead configuration data (locations, attributes, etc.)
webhooksManage webhook subscriptions
report-builder:readExecute 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
ParameterTypeDefaultMaxDescription
limitinteger20100Results per page
offsetinteger010000Number 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=true

Syncing Data

Use updatedSince to fetch only records modified after a specific timestamp:

GET /clients?updatedSince=2024-12-01T00:00:00Z

Recommended sync pattern:

  1. Initial sync — Paginate through all records, store the sync timestamp
  2. Incremental syncs — Use updatedSince with your stored timestamp
  3. 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_time

Note: Deletion tracking is not currently supported. For applications requiring deletion awareness, perform periodic full syncs to reconcile your local data.

Rate Limits

Endpoint TypeDefault 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 retryAfter value 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

CodeMeaning
200Success
201Created
204Deleted (no content)
400Bad request / Validation error
401Invalid or missing API key
403Forbidden (insufficient scope or locked resource)
404Resource not found
409Conflict (e.g., duplicate entry, invalid state transition)
429Rate limited
500Server error

Common Error Codes

CodeDescription
VALIDATION_ERRORRequest body failed validation
NOT_FOUNDResource doesn't exist
UNAUTHORIZEDAPI key invalid or missing
INSUFFICIENT_PERMISSIONSAPI key lacks required scope
LOCKEDResource is locked and cannot be modified
RATE_LIMITEDToo many requests
ALREADY_EXISTSDuplicate 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:

CategoryEvents
Clientsclient.created, client.updated, client.status_changed
Caregiverscaregiver.created, caregiver.activated, caregiver.terminated
Visitsvisit.created, visit.updated, visit.completed, visit.cancelled
EVVevv.check_in, evv.check_out
Authorizationsauthorization.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 clients

JavaScript: 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

  1. Server-side proxy — Never expose API keys in mobile apps. Route requests through your backend.
  2. Offline sync — Use updatedSince for efficient data synchronization
  3. EVV capture — Use the EVV endpoints to record GPS-verified check-in/out

Billing System Integration

  1. Fetch visits — Get completed visits with GET /visits?hasCheckOut=true
  2. Get authorization — Verify hours against GET /authorizations/{id}
  3. Export data — Use pagination to export billing-ready data

HR System Integration

  1. Sync caregivers — Fetch active caregivers with GET /caregivers?status=active
  2. Track certifications — Monitor expiring credentials
  3. Webhook alerts — Subscribe to caregiver.terminated events

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?