Getting Started
The SSN API lets approved clients create and manage sites, site visits, messages, file uploads, and webhook subscriptions through a tenant-scoped REST API.
This guide walks through the first successful integration path:
- Choose the correct environment.
- Request an access token.
- Create a site.
- Create a site visit for that site.
- Optionally assign one or more site assets to the visit.
- Look up the visit by your ticket reference.
- Add a message.
Environments
Use sandbox while building and testing your integration.
| Environment | Base URL |
|---|---|
| Sandbox | https://sandbox-api.siteservicesnow.com/api/v1 |
| Production | https://api.siteservicesnow.com/api/v1 |
Sandbox and production use separate credentials. A token from one environment cannot be used against the other environment.
Credentials
SSN will provide a credential packet for each environment:
client_idclient_secret- allowed scopes
- initial valid
projectIdanditemNamevalues for smoke testing - optional test
siteAssetIdsfor smoke testing the asset-selection workflow
Site asset assignment uses Site Visits scopes: site-visits:write lets you submit siteAssetIds, and site-visits:read lets you read returned siteVisitAssets. There are no separate site asset scopes.
Store the client secret securely. Do not send it in browser code or expose it in client-side applications.
You can also discover current valid references through the API:
GET /api/v1/projectsreturns active project references valid forsite-visits.projectIdGET /api/v1/itemsreturns item references valid forsite-visits.itemName
Both endpoints require the site-visits:read scope.
Authentication
Use the OAuth2 client credentials flow.
POST /api/v1/oauth/token
Content-Type: application/x-www-form-urlencoded
grant_type=client_credentials&client_id=YOUR_CLIENT_ID&client_secret=YOUR_CLIENT_SECRET
Successful response:
{
"access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
"token_type": "Bearer",
"expires_in": 3600,
"scope": "sites:read sites:write site-visits:read site-visits:write"
}
Send the token on API requests:
Authorization: Bearer YOUR_ACCESS_TOKEN
First API Check
Confirm your credential can read sites:
GET /api/v1/sites
Authorization: Bearer YOUR_ACCESS_TOKEN
An empty list is valid for a new client:
{
"data": [],
"meta": {
"pageSize": 25
}
}
Create a Site
Create a site using your own stable site identifier in siteCode.
POST /api/v1/sites
Authorization: Bearer YOUR_ACCESS_TOKEN
Idempotency-Key: 6d0c49d5-0a35-4c28-93e5-0ef6c8b04cd3
Content-Type: application/json
{
"name": "Demo Store 100",
"siteCode": "DEMO-STORE-100",
"addressLine1": "123 Main St",
"city": "Phoenix",
"stateRegion": "AZ",
"postalCode": "85001",
"country": "US"
}
Important rules:
siteCodemust be unique for your client account.- The API derives client ownership from your access token.
- Do not send a tenant ID or Quickbase record ID.
Create a Site Visit
Create a site visit by referencing the site through clientSiteId.
This value should match the siteCode from the site you created.
POST /api/v1/site-visits
Authorization: Bearer YOUR_ACCESS_TOKEN
Idempotency-Key: 0f7d1a6e-9642-41ba-aed5-77aa357c1ca6
Content-Type: application/json
{
"clientSiteId": "DEMO-STORE-100",
"description": "Replace damaged kiosk screen",
"clientTicketNumber": "CLIENT-TKT-10027",
"serviceType": "Break / Fix",
"jobInstructions": "Check in with store manager before opening the equipment.",
"projectId": 123,
"itemName": "TEST ITEM",
"startDate": "2026-04-15T16:00:00Z",
"dueDate": "2026-04-16T01:00:00Z",
"arePartsRequired": false,
"partsReturnRequired": false,
"schedulingRule": "Date/Time Specific"
}
Important rules:
clientSiteIdmust match a site owned by your client account.projectIdmust belong to your client account and be active.itemNamemust belong to your client account.clientTicketNumberis optional but should be unique for your client account.
Assign Site Assets to a Site Visit
If SSN has configured Site Assets records for the site, you can assign them
during create or patch by sending siteAssetIds.
{
"clientSiteId": "DEMO-STORE-100",
"description": "Replace damaged kiosk screen",
"clientTicketNumber": "CLIENT-TKT-10027",
"serviceType": "Break / Fix",
"jobInstructions": "Check in with store manager before opening the equipment.",
"projectId": 123,
"itemName": "TEST ITEM",
"startDate": "2026-04-15T16:00:00Z",
"dueDate": "2026-04-16T01:00:00Z",
"arePartsRequired": false,
"partsReturnRequired": false,
"schedulingRule": "Date/Time Specific",
"siteAssetIds": [456, 457]
}
GET /api/v1/site-visits/{id} returns both:
assetsToService: human-readable summary of assigned assetssiteVisitAssets: detailed child rows includingsiteAssetId,uniqueId,friendlyLocation,assetStatus, andassetDisplay
These fields are part of the Site Visits resource. They are controlled by site-visits:read, not by separate asset scopes.
Important rules:
- every submitted
siteAssetIdmust belong to the same site as the site visit - omitting
siteAssetIdson patch leaves assignments unchanged - sending
siteAssetIds: []clears assigned assets - cross-site asset assignment is rejected with
409 Conflict
Look Up a Site Visit by Client Ticket Number
If you supplied clientTicketNumber, you can use it to look up the visit later.
GET /api/v1/site-visits?clientTicketNumber=CLIENT-TKT-10027
Authorization: Bearer YOUR_ACCESS_TOKEN
Add a Message
Messages are created against an existing site visit.
POST /api/v1/messages
Authorization: Bearer YOUR_ACCESS_TOKEN
Idempotency-Key: 2a24d0e9-233c-42c7-a4a3-fd579debd8ad
Content-Type: application/json
{
"siteVisitId": "visit_0d995b97a85e4c64ad5594c2e74b69e4",
"type": "Support",
"messageThread": "Client note: technician should call before arrival.",
"status": "Open",
"urgency": "Normal",
"buyer": true,
"clientContacts": true
}
The API sets these values internally:
method = EmailoccurredAt = nowssnPm = true
Upload Files
Site-visit return labels:
POST /api/v1/site-visits/{id}/return-label
Authorization: Bearer YOUR_ACCESS_TOKEN
Content-Type: multipart/form-data
Message attachments:
POST /api/v1/messages/{id}/file-attachment
Authorization: Bearer YOUR_ACCESS_TOKEN
Content-Type: multipart/form-data
Use form field name file.
Supported file types:
- return labels: PDF
- message attachments: PDF, PNG, JPEG
Webhooks
Webhook delivery is available for clients with webhook scopes.
Current live event:
ticket.created
Webhook deliveries use an event envelope:
{
"id": "evt_...",
"type": "ticket.created",
"occurredAt": "2026-04-03T16:19:30.074Z",
"resourceType": "site-visits",
"resourceId": "visit_...",
"payloadVersion": "2026-03-24",
"data": {}
}
Clients should:
- verify the HMAC signature
- deduplicate by event ID
- respond with a 2xx status code when the event is accepted
Common Error Cases
| Status | Meaning |
|---|---|
401 | Missing or invalid bearer token |
403 | Credential does not have the required scope |
404 | Referenced client-owned site, project, item, or resource was not found |
409 | Duplicate resource, optimistic concurrency conflict, or invalid site-asset assignment |
422 | Request validation failed |
Next Steps
After this first path works in sandbox:
- Test duplicate-site behavior.
- Test invalid project and invalid item error handling.
- Test valid and invalid
siteAssetIdsbehavior if your workflow uses site assets. - Test message creation and patching.
- Configure webhook delivery if your integration needs event notifications.
- Request production credentials when sandbox validation is complete.