Skip to main content

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:

  1. Choose the correct environment.
  2. Request an access token.
  3. Create a site.
  4. Create a site visit for that site.
  5. Optionally assign one or more site assets to the visit.
  6. Look up the visit by your ticket reference.
  7. Add a message.

Environments

Use sandbox while building and testing your integration.

EnvironmentBase URL
Sandboxhttps://sandbox-api.siteservicesnow.com/api/v1
Productionhttps://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_id
  • client_secret
  • allowed scopes
  • initial valid projectId and itemName values for smoke testing
  • optional test siteAssetIds for 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/projects returns active project references valid for site-visits.projectId
  • GET /api/v1/items returns item references valid for site-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:

  • siteCode must 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:

  • clientSiteId must match a site owned by your client account.
  • projectId must belong to your client account and be active.
  • itemName must belong to your client account.
  • clientTicketNumber is 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 assets
  • siteVisitAssets: detailed child rows including siteAssetId, uniqueId, friendlyLocation, assetStatus, and assetDisplay

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 siteAssetId must belong to the same site as the site visit
  • omitting siteAssetIds on 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 = Email
  • occurredAt = now
  • ssnPm = 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

StatusMeaning
401Missing or invalid bearer token
403Credential does not have the required scope
404Referenced client-owned site, project, item, or resource was not found
409Duplicate resource, optimistic concurrency conflict, or invalid site-asset assignment
422Request validation failed

Next Steps

After this first path works in sandbox:

  1. Test duplicate-site behavior.
  2. Test invalid project and invalid item error handling.
  3. Test valid and invalid siteAssetIds behavior if your workflow uses site assets.
  4. Test message creation and patching.
  5. Configure webhook delivery if your integration needs event notifications.
  6. Request production credentials when sandbox validation is complete.