Get and query assessments
Two reads exist. GET /v1/assessments/{assessment_id} returns the latest version of one assessment and is the authoritative way to poll a pending answer or refresh one a webhook told you about. POST /v1/assessments/query pages through every unexpired assessment of one account. Both are in the interactive reference.
Get one assessment
Section titled “Get one assessment”GET /v1/assessments/{assessment_id} needs the assess or the history scope. A key with only assess can read the assessments it created without being able to list an account’s history.
const assessment = await nr.assessments.get("asm_01JB3C4D5E6F7G8H9JKMNPQRST");
// Poll until it is no longer pending, honouring Retry-After. Gives up after 30 s by default.const settled = await nr.assessments.wait("asm_01JB3C4D5E6F7G8H9JKMNPQRST", { timeoutMs: 10_000 });assessment = client.assessments.get("asm_01JB3C4D5E6F7G8H9JKMNPQRST")
# Poll until it is no longer pending, honouring Retry-After. Gives up after 30 s by default.settled = client.assessments.wait("asm_01JB3C4D5E6F7G8H9JKMNPQRST", timeout=10.0)res, err := client.Assessments.Get(ctx, "asm_01JB3C4D5E6F7G8H9JKMNPQRST", nil)
// Poll until it is no longer pending, honouring Retry-After. Gives up after 30 s by default, or when ctx ends.waitCtx, cancel := context.WithTimeout(ctx, 10*time.Second)defer cancel()settled, err := noresponse.WaitForAssessment(waitCtx, client, "asm_01JB3C4D5E6F7G8H9JKMNPQRST", nil)curl https://api.noresponse.org/v1/assessments/asm_01JB3C4D5E6F7G8H9JKMNPQRST \ -H "Authorization: Bearer $NORESPONSE_API_KEY"Parameters
Section titled “Parameters”| Parameter | In | Required | Value |
|---|---|---|---|
assessment_id |
path | Yes | asm_ and 26 characters, matched case-sensitively. A value of any other shape answers 404, not 400. |
response_mode |
query | No | standard (default) or detailed. detailed needs the detailed scope as well. |
Response
Section titled “Response”200 OK with the assessment object at its highest assessment_version. While status is pending, or partial with a source still continuing, the response carries Retry-After; poll no faster than it says. The SDK wait helpers poll on Retry-After, with a one second floor when the header is absent, and stop at their timeout with a timeout error.
| Status | Code | When |
|---|---|---|
| 401 | unauthorized |
No key, or a malformed, unknown, revoked or expired key. |
| 403 | tenant_forbidden |
The key has neither assess nor history, or asked for detailed without the scope. |
| 404 | not_found |
See below. |
| 429 | rate_limited |
Retry after Retry-After. |
| 503 | service_unavailable |
Retry after Retry-After. |
404 and expiry
Section titled “404 and expiry”An unknown id, an expired assessment, a malformed id and another organisation’s assessment all answer the same 404 not_found.
An assessment expires at its expires_at at the latest: created_at plus your retention period when it was created, 365 days by default. If you shorten your retention period it is gone sooner. After that moment it is gone. GET answers 404, the assessment leaves history, and one last assessment.updated webhook with status: "expired" closes it. A later revision keeps the original expires_at, and a new assessment for the same account does not renew an older one. See Data retention.
Query an account’s history
Section titled “Query an account’s history”POST /v1/assessments/query needs the history scope. The account id travels in the body rather than the URL, so it stays out of access logs and proxies. The request is read-only and safe to retry; it takes no Idempotency-Key.
// One page.const page = await nr.assessments.query({ accountId: "usr_8f2k1", limit: 20 });console.log(page.items.length, page.hasMore, page.nextCursor);
// Every item, across pages.for await (const assessment of await nr.assessments.query({ accountId: "usr_8f2k1" })) { console.log(assessment.assessmentId, assessment.status, assessment.risk?.score);}# One page.page = client.assessments.query(account_id="usr_8f2k1", limit=20)print(len(page.items), page.has_more, page.next_cursor)
# Every item, across pages.for assessment in client.assessments.query(account_id="usr_8f2k1"): print(assessment.assessment_id, assessment.status)
# Async: query_async returns a page that is an async iterator.async for assessment in await client.assessments.query_async(account_id="usr_8f2k1"): print(assessment.assessment_id)// One page.res, err := client.Assessments.Query(ctx, components.AssessmentQuery{AccountID: "usr_8f2k1", Limit: noresponse.Pointer(int64(20))})if err != nil { return err}page := res.Resultfmt.Println(len(page.Items), page.NextCursor, page.RetentionDays)
// Every item, across pages.for assessment, err := range noresponse.AllAssessments(ctx, client, components.AssessmentQuery{AccountID: "usr_8f2k1"}) { if err != nil { return err } fmt.Println(assessment.AssessmentID, assessment.Status)}curl https://api.noresponse.org/v1/assessments/query \ -H "Authorization: Bearer $NORESPONSE_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "account_id": "usr_8f2k1", "limit": 20 }'
# The next page: pass next_cursor back unchanged.curl https://api.noresponse.org/v1/assessments/query \ -H "Authorization: Bearer $NORESPONSE_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "account_id": "usr_8f2k1", "limit": 20, "cursor": "eyJiIjoiY2hrXzAxIn0" }'| Field | Type | Required | Value |
|---|---|---|---|
account_id |
string | Yes | The account to list, in the same shape as on create. |
limit |
integer | No | 1 to 50 items per page. Default 20. |
cursor |
string | No | next_cursor from the previous page, unchanged. 1 to 256 characters of letters, digits, _ and -. |
response_mode |
string | No | standard (default) or detailed. detailed needs the detailed scope as well as history. |
Response
Section titled “Response”200 OK with one page:
{ "schema_version": "1", "account_id": "usr_8f2k1", "items": [ { "assessment_id": "asm_01JA2Z8K6Q4V7W9XBCDEFGHJKM", "status": "complete", "recommendation": "step_up", "...": "..." }, { "assessment_id": "asm_01JA2Z9Y3R5T6V7W8X9ZABCDEF", "status": "complete", "recommendation": "allow", "...": "..." } ], "next_cursor": "eyJiIjoiY2hrXzAxIn0", "retention_days": 365}| Field | Type | Meaning |
|---|---|---|
schema_version |
string | "1". |
account_id |
string | The account you asked about. Every item’s account_id equals it. |
items |
array | At most limit assessment objects, newest first, each at its latest assessment_version. Only unexpired assessments are listed. An account with none answers 200 with an empty array, not 404. |
next_cursor |
string | Present only when another page exists. Opaque, integrity-protected and bound to your organisation and this account_id. Do not parse it. |
retention_days |
integer | Your current retention period, 1 to 365, so you know how far back history reaches. No item expires later than its created_at plus this many days. |
| Status | Code | When |
|---|---|---|
| 400 | invalid_request |
The body breaks a rule above. |
| 400 | invalid_cursor |
The cursor was changed, was issued for another account or organisation, or has expired. Start again without one. |
| 401 | unauthorized |
No key, or a malformed, unknown, revoked or expired key. |
| 403 | tenant_forbidden |
The key lacks history, or detailed when asked for. |
| 413 | payload_too_large |
The body is over 32,768 bytes. |
| 415 | unsupported_media_type |
Content-Type is not application/json. |
| 429 | rate_limited |
Retry after Retry-After. |
| 503 | service_unavailable |
Retry after Retry-After. |
Cursor pagination
Section titled “Cursor pagination”A page carries next_cursor only when more items exist. To read the next page, send the same account_id and limit with cursor set to that value. When a page has no next_cursor, you have everything.
The SDK iterators do this for you: for await over the query in TypeScript, plain iteration over the page in Python, and noresponse.AllAssessments in Go, which returns an iter.Seq2 of assessment and error. Each fetches the next page as you reach the end of the current one, so a long history costs one request per 50 items and no more memory than one page.