Skip to main content

Siteline API for 3rd Parties

This article explains how to authenticate, query, and work within the limitations of Siteline’s GraphQL API, with examples, schema references, and FAQs to help third-party developers integrate successfully.

Bradley LaFave avatar
Written by Bradley LaFave
Updated over 4 months ago

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 Authorization header:

Authorization: Bearer <your token>

Contact your Siteline account partner to receive your company’s API key.


Limitations

  1. Rate Limits: Maximum of 2 requests per second. Exceeding this will return a 429 HTTP response.

  2. Cold Start: Initial API calls may take up to 20s due to scaling. Subsequent calls should be under 1s.

  3. Query Depth: GraphQL queries are limited to 4 levels of nesting. Going beyond this returns a 400 HTTP response.

✅ 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


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 null if 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.

Did this answer your question?