Overview
Siteline uses a GraphQL API to read and write data between the frontend and backend systems. This API enables third parties to integrate directly with Siteline.
Base URL:
https://api-external.siteline.com/graphql
Authentication
All API requests require an API key.
Pass the key as a Bearer token in the
Authorizationheader:
Authorization: Bearer <your token>
Contact your Siteline account partner to receive your company’s API key.
Limitations
Rate Limits: Maximum of 2 requests per second. Exceeding this will return a
429 HTTPresponse.Cold Start: Initial API calls may take up to 20s due to scaling. Subsequent calls should be under 1s.
Query Depth: GraphQL queries are limited to 4 levels of nesting. Going beyond this returns a
400 HTTPresponse.
✅ Example (allowed – 4 levels deep):
query currentCompany {
currentCompany {
# level 1
id
users {
# level 2
company {
# level 3
users {
# level 4
id
}
}
}
}
}
❌ Example (not allowed – 5 levels deep):
query currentCompany {
currentCompany {
# level 1
id
users {
# level 2
company {
# level 3
users {
# level 4
company {
# level 5 -- this query is too deeply nested
id
}
}
}
}
}
}GraphQL API Schema
Schema: View schema in Notion
Postman Collection: Contact [email protected] for access.
Video Walkthrough: Loom demo
Example: Node.js Request
fetch("https://api-external.siteline.com/graphql", {
method: "POST"
headers: {
authorization: "Bearer <your token>",
"content-type": "application/json",
},
body: JSON.stringify({
query: `
query getCurrentCompany {
currentCompany: { id, name }
}
`
})
});Queries
1. currentCompany
Purpose: Verify authentication.
# Query
query currentCompany {
currentCompany {
id
name
}
}
# Response
{
"data": {
"currentCompany": {
"id": "60d45759-f8d4-4106-8858-0f02d2374a4a",
"name": "Manheim Glass"
}
}
}
2. paginatedContracts
Retrieves contracts with optional filters.
Supports pagination (50 per page).
You can provide filters to only get the ones you care about.
# Query
query paginatedContracts($input: GetPaginatedContractsInput!) {
paginatedContracts(input: $input) {
cursor
hasNext
contracts {
id
internalProjectNumber
billingType
percentComplete
project {
projectNumber
}
payApps {
id
billingStart
billingEnd
timeZone
status
}
}
}
}
# Input
{
"input": {
"month": "2023-04",
"payAppStatus": "SUBMITTED_SYNCED_PAID",
"contractStatus": "ACTIVE",
"limit": 50,
"cursor": null // Or the last contract ID was fetched
}
}
# Response
{
"data": {
"paginatedContracts": {
"cursor": "7dcf6d35-935a-43bc-a833-c21c1612f343",
"hasNext": true,
"contracts": [
{
"id": "531dce87-8867-4a59-96e6-313b094a1687",
"internalProjectNumber": "21030161",
"billingType": "LUMP_SUM",
"percentComplete": 1,
"project": {
"projectNumber": "029-393-08505"
},
"payApps": [
{
"id": "b8eb2ebf-14b5-46a3-9bed-9003b14d30d5",
"billingStart": "2022-12-01T06:00:00.000Z",
"billingEnd": "2023-01-01T05:59:59.999Z",
"timeZone": "America/Chicago",
"status": "PAID"
},
...
]
},
...
]
}
}
}
3. contract(id)
Gets details for a single contract.
You probably don’t need this if you already selected the pay app IDs from the previous query.
# Query
query contract($id: ID!) {
contract(id: $id) {
id
payApps {
id
}
}
}
# Input
{
"id": "257ff7ac-dc3e-4a21-b209-6cadfe255a6d"
}
# Response
{
"data": {
"contract": {
"id": "257ff7ac-dc3e-4a21-b209-6cadfe255a6d",
"payApps": [
{
"id": "5b5164bd-16eb-48bd-96a7-45a3929d3e5d"
},
{
"id": "6d88cdce-4c85-4829-abfe-c7809a054d16"
},
{
"id": "1c932f65-d3f3-4884-a5a4-4564975b288b"
}
]
}
}
}
4. payApp(id)
Retrieves details of a single pay app, including billing values and SOV line items.
# Query
query payApp($id: ID!) {
payApp(id: $id) {
id
status
billingStart
billingEnd
timeZone
payAppNumber
currentBilled
currentRetention
totalRetention
previousRetentionBilled
retentionOnly
submittedAt
progress {
id
progressBilled
storedMaterialBilled
totalValue
sovLineItem {
id
code
name
}
}
}
}
# Input
{
"id": "5514674b-a496-4fd8-987a-d5421b639ecc"
}
# Response
{
"data": {
"payApp": {
"id": "5514674b-a496-4fd8-987a-d5421b639ecc",
"status": "PROPOSED",
"billingStart": "2023-03-01T06:00:00.000Z",
"billingEnd": "2023-04-01T04:59:59.999Z",
"timeZone": "America/Chicago",
"payAppNumber": 1,
"currentBilled": 563600,
"currentRetention": 28180,
"totalRetention": 28180,
"previousRetentionBilled": 0,
"retentionOnly": false,
"submittedAt": "2023-03-21T20:39:46.083Z",
"progress": [
{
"id": "fdaa7977-e8ef-40eb-a8c0-ab9070621d76",
"progressBilled": 0,
"storedMaterialBilled": 563600,
"totalValue": 3506800,
"sovLineItem": {
"id": "ff3ffb87-5a7d-4040-b749-f0429999851b",
"code": "1",
"name": "Materials & Labor"
}
}
]
}
}
}
5. paginatedPayApps
Retrieves a paginated list of pay apps with filters.
You can provide filters to only get the ones you care about.
# Query
query paginatedPayApps($input: GetPaginatedPayAppsInput!) {
paginatedPayApps(input: $input) {
cursor
hasNext
payApps {
id
# ...
}
}
}
# Input
{
# Note: all fields in `input` are optional.
# If you don't provide a filter, all pay apps are returned (up to the pagination limit).
# If you just need pay apps submitted in a specific month, only pass `submittedInMonth`, you don't need status/paidInMonth.
# Note that submittedInMonth also includes syncing to a GC portal, which we consider a submission.
"input": {
"status": "PROPOSED", # Or PAID, or SYNCED
"submittedInMonth": "2020-01", # Month in which the pay app was submitted. This also applies to paid pay apps
"paidInMonth": "2020-01", # Month in which the pay app was marked as paid
"limit": 50,
"cursor": null // Or the last pay app ID was fetched
}
}
# Response
{
"data": {
"paginatedPayApps": {
"cursor": "7dcf6d35-935a-43bc-a833-c21c1612f343",
"hasNext": true,
"payApps": [
{
"id": "531dce87-8867-4a59-96e6-313b094a1687",
# ...
}
...
]
}
}
}
FAQ
Q: How do I know when a pay app was submitted?
Use the field
payApp.submittedAt.Returns the most recent time a pay app was proposed or synced to a GC portal.
Returns
nullif currently in draft, even if previously submitted.
Q: Can I use the API to update or push data into Siteline?
No. The Siteline API is read-only for third parties. You can pull and query data, but you cannot create, update, or push data back into Siteline.