Requests and Responses
Learn how Scrut API responses are structured, and how to filter results, add extra fields, paginate, and work with timestamps.
Every /v1 endpoint follows the same conventions for requests and responses. Learn them once, and they apply across policies, controls, evidence, frameworks, tests, and vulnerabilities.
Request Basics
- Send every request to your region's base URL. See Base URLs.
- Include your access token in the
Authorizationheader asBearerfollowed by the token. See Authentication and Access Tokens. - Send JSON request bodies with
Content-Type: application/json. Evidence uploads usemultipart/form-datainstead. - Include an
Idempotency-Keyheader on every write request. See Idempotency.
Response Format
Successful /v1 responses use a { data, meta } envelope:
{
"data": {
"evidenceId": "37935520-e7d4-40e4-b054-ce546528ac42",
"modifiedOn": 1715904123000
},
"meta": {
"requestId": "550e8400-e29b-41d4-a716-446655440000"
}
}
datais an object for a single resource and an array for lists.meta.requestIdis a unique ID for each request. The same value is returned in theX-Request-Idresponse header.- The vulnerabilities list also returns pagination fields in
meta. - JSON field names use camelCase.
- The HTTP status code tells you whether a request succeeded. Response bodies don't include a
successflag. - Download URLs for files hosted by Scrut are never returned. Document and attachment metadata includes a
urlonly for external links you added yourself. - The token endpoint,
/oauth/token, returns the standard OAuth token object and doesn't use this envelope.
Failed requests return an error object instead of data. See Errors.
Filtering
List endpoints accept filters as query parameters. Pass multiple values as a comma-separated list.
| Endpoint | Filters |
|---|---|
| Policies, Evidence | status, frameworkIds, controlIds, entityIds, isRelevant, assigneeEmails, approverEmails, departments, nextReviewAtFrom, nextReviewAtTo |
| Controls | status, frameworkIds, entityIds, assigneeEmails, domains, functionGroupings, controlScope |
| Frameworks | entityIds |
| Tests | status, frameworkIds, assigneeEmails, applications |
| Vulnerabilities | status, severity, source, fixAvailable |
For example, to list published policies and policies that need review:
curl "https://api.scrut.io/v1/policies?status=published,needs_review" \
-H "Authorization: Bearer $SCRUT_ACCESS_TOKEN"
Filter Rules
- In
assigneeEmailsandapproverEmails, encode+in plus-addressed emails as%2B. nextReviewAtFromandnextReviewAtToare inclusive bounds in Unix epoch milliseconds.- Use
No Departmentindepartmentsto match items without a department. - Test status values are
passing,fix_required,ignored, andaffected. A test shows asaffectedwhen it passes, but a connected observability integration for automated tests is failing.
Filter Resources on a Single Record
On get test and get vulnerability, use resourceStatus to filter the resources returned:
- Get test returns
fix_requiredresources by default. - On get vulnerability, request
affectedResourcesinfields. Scrut returnsopenandriskresources by default.
Extra Fields
Use the fields parameter to add data that isn't returned by default. Pass multiple values as a comma-separated list.
- Extra list columns add fields to each record in a list response.
- Get expansions add related data to a single-record response.
| Resource | Extra list columns | Get expansions |
|---|---|---|
| Policies | policyBehavior, mappedFrameworkIds, mappedControlIds, notRelevantReason, versionNumber, effortEstimate, recurrence, source, publishedBy, publishedOn, addedBy, addedOn, lastModifiedBy, modifiedOn | aiDetectedGaps, documents, versionHistory, tickets |
| Controls | mappedFrameworkIds, outOfScopeReason, markedOutOfScopeBy, addedBy, addedOn, lastModifiedBy, modifiedOn | complianceOverview, mappedArtifacts, mappedRisks |
| Evidence | mappedFrameworkIds, mappedControlIds, notRelevantReason, recurrence, effortEstimate, source, evidenceCollectionMethod, ticketsCount, addedBy, addedOn, lastModifiedBy, modifiedOn | mappedTests, gaps, attachments, tickets |
| Frameworks | addedBy, addedOn, lastModifiedBy, modifiedOn, tscSelections | Not applicable |
| Tests | mappedFrameworkIds, effortEstimate, ignoreReason, addedOn, modifiedOn, ticketsCount | tickets, mappedRisks, testHistory |
| Vulnerabilities | cvssScore, resourcesCount, firstSeen | affectedResources, tickets, mappedRisks |
For example, to include mapped framework IDs and the publish date in a policy list:
curl "https://api.scrut.io/v1/policies?fields=mappedFrameworkIds,publishedOn" \
-H "Authorization: Bearer $SCRUT_ACCESS_TOKEN"
Pagination
The vulnerabilities list is the only paginated endpoint. All other list endpoints return every matching record in a single array.
- Set
countto50or100to choose the page size. The default is50. - To fetch the next page, pass
meta.nextCursorfrom the previous response as thenextCursorquery parameter. OmitnextCursoron the first page. meta.nextCursorisnullon the last page.meta.totalCountis the number of findings that match your filters across all pages.
curl "https://api.scrut.io/v1/vulnerabilities?severity=critical,high&count=100&nextCursor=$NEXT_CURSOR" \
-H "Authorization: Bearer $SCRUT_ACCESS_TOKEN"
Timestamps
Dates in request bodies, response bodies, and query filters are Unix epoch milliseconds unless noted otherwise. For example, 1735689600000 is January 1, 2025, 00:00:00 UTC.
The one exception is the X-RateLimit-Reset header, which is a Unix timestamp in seconds. See Rate Limits.
IDs in the Path
Use IDs exactly as the API returns them.
Heads Up! The vulnerabilityId returned by list, get, and create is already URL-encoded, for example api%23VUL-5672. Paste it into the path as is. Encoding it again causes a 404 not_found.
Contact support@scrut.io for further assistance.