OSPulse

Filtering & sorting

Collection endpoints narrow their results with query parameters. There is no cross-cutting query language and no generic sort parameter — each endpoint declares the filters that make sense for its resource, and the interactive reference is the authoritative list of which ones it accepts.

Unknown parameters are ignored, not rejected

Sending a filter an endpoint does not support does not return an error. The parameter is discarded and you receive the unfiltered collection. If a filter appears to have no effect, check its spelling and its presence in the reference before assuming the data is wrong.

How filtering works

PropertyBehaviour
LocationQuery string. Filters are never sent in a body, including on POST action endpoints.
NamingcamelCase, matching the response field they filter on where one exists.
OptionalityEvery filter is optional. Omitting all of them returns the collection unfiltered.
CombinationMultiple filters combine with AND. There is no OR across different parameters.
Empty resultA filter that matches nothing returns 200 with an empty items array — not 404.
Invalid valueA value that cannot be parsed, or that is outside an accepted enum, returns 400.
CaseEnum-valued filters are matched against the documented PascalCase values. Free-text search is case-insensitive.

Filter categories

The parameters differ per endpoint, but they fall into a small number of recognisable shapes. The examples below are real parameters on the endpoints named; treat them as the pattern rather than as a universal vocabulary.

Enumerated attribute filters

Narrow to one member of a closed set. The accepted values are the enum members listed in the reference for that parameter.

bash
# Packages in a single ecosystem
curl "https://api.ospulse.app/api/v1/packages?ecosystem=Npm&take=50" \
  -H "Authorization: Bearer $OSPULSE_TOKEN"
EndpointParameterFilters by
GET /api/v1/packagesecosystemPackage ecosystem — npm, NuGet, PyPI, Maven, Go, Cargo, RubyGems, Composer, Docker.
GET /api/v1/repositoriesbusinessCriticalityThe criticality tier recorded against the repository.
GET /api/v1/repositorieslifecycleStatusWhere the repository sits in its lifecycle.
GET /api/v1/cra/findingsclassCRA classification of the finding.
GET /api/v1/repositories/{repositoryId}/quantum/certificatesclassificationPost-quantum classification of the certificate.
GET /api/v1/repositories/{repositoryId}/quantum/certificatesprovenanceHow the certificate was observed or supplied.

Enum values move forward

New enum members are added without a breaking-change bump — a new ecosystem, a new lifecycle state. Do not hard-code an exhaustive list of accepted values in a client; read them from the spec, or treat an unrecognised value as pass-through. See Conventions.

Status and workflow filters

Most resources with a workflow expose the current state as a filter, which is how you build a work queue.

EndpointParameterTypical use
GET /api/v1/scansstateShow only running or only failed scans.
GET /api/v1/cra/findingsstateSeparate findings awaiting action from those already acknowledged.
GET /api/v1/repositories/{repositoryId}/quantum/findingsstatusFilter findings by their triage state.
GET /api/v1/quantum/tls-observationsstateFilter observations by their assessment state.
bash
# Scans currently in flight for one repository
curl "https://api.ospulse.app/api/v1/scans?repositoryId=3f8a1c27-9c1b-4e2a-8f6d-2a7c5e9b0d14&state=Running" \
  -H "Authorization: Bearer $OSPULSE_TOKEN"

Severity filters

Where a resource carries a severity, it can usually be filtered on directly — the fastest way to build an alerting integration that only reacts to what matters.

bash
curl "https://api.ospulse.app/api/v1/repositories/$REPO_ID/quantum/findings?severity=Critical&take=50" \
  -H "Authorization: Bearer $OSPULSE_TOKEN"

Severity is a filter, not an ordering: passing severity=Critical returns only critical rows in the endpoint's default order, it does not sort a mixed set with critical first.

Relationship filters

Scope a collection to a parent resource by passing its identifier.

EndpointParameterScopes to
GET /api/v1/scansrepositoryIdOne repository's scans.
GET /api/v1/repositoriesworkspaceId, teamIdOne workspace or team.
GET /api/v1/cra/findingsproductIdOne CRA product.
GET /api/v1/cra/duties, GET /api/v1/cra/annex-iproductIdOne product's duties or Annex I items.
GET /api/v1/repositories/{repositoryId}/quantum/findingsscanIdFindings from a specific scan run.

Note the two ways a relationship is expressed. Where a resource belongs unambiguously to a parent it appears as a path segment (/api/v1/repositories/{repositoryId}/quantum/findings); where it can be listed globally and optionally narrowed it appears as a query parameter (/api/v1/scans?repositoryId=…).

GET /api/v1/packages accepts search, which matches against the package name. It is a substring match, not a query language — no operators, no wildcards, no field prefixes.

bash
curl "https://api.ospulse.app/api/v1/packages?search=lodash&take=20" \
  -H "Authorization: Bearer $OSPULSE_TOKEN"

Combine it with ecosystem to disambiguate names that exist in more than one registry.

For questions that a substring match cannot express, POST /api/v1/ai/query accepts a natural-language question over your tenant's data. It is a different tool with different guarantees — useful for exploration, not for a deterministic integration.

Time-window filters

A few endpoints narrow by time rather than by attribute. Values are ISO-8601 UTC and must be URL-encoded.

EndpointParameterEffect
GET /api/v1/scans/{scanId}/logssinceUtcReturn log entries after an instant — the basis of a tailing loop.
GET /api/v1/compliance/controls/{controlId}/evidenceperiodStart, periodEndBound evidence to a reporting period.
GET /api/v1/cra/clocks/duewithinHoursReporting clocks falling due inside a rolling window.
bash
curl "https://api.ospulse.app/api/v1/scans/$SCAN_ID/logs?sinceUtc=2026-03-14T09%3A21%3A47Z&take=200" \
  -H "Authorization: Bearer $OSPULSE_TOKEN"

Time-window filters are the one case where re-reading a collection is genuinely safe under concurrent writes: advance sinceUtc to the timestamp of the last entry you processed and the next call returns only what is new.

Result-shaping parameters

Not every query parameter is a filter. Some bound the work the endpoint does, and they behave differently — exceeding the bound truncates the result rather than returning an error.

EndpointParameterEffect
GET /api/v1/repositories/{repositoryId}/dependency-graphmaxEdgesCaps graph traversal so a deep transitive tree cannot produce an unbounded response.
GET /api/v1/analytics/export/destinations/{id}/runslimitCaps the number of recent runs returned.
GET /api/v1/cra/reports/{packId}formatSelects the output format of the downloaded artefact.

Sorting

The API does not expose a general sort parameter. Each collection endpoint applies an ordering appropriate to its resource — most commonly newest-first for event-like collections such as scans, logs, and alerts, and a stable natural ordering for catalogue-like collections such as packages.

Consequences for a client:

  • Do not depend on order beyond what the reference states. An ordering that is not documented is an implementation detail and may change.
  • Sort client-side for presentation. Fetch the page, then order it for display. This is correct for a UI and cheap at typical page sizes.
  • Do not sort client-side to rank a whole collection. Sorting one page of 50 out of 428 rows ranks that page, not the collection. To find the highest-severity items, filter by severity — do not fetch an arbitrary page and sort it.
  • For portfolio-wide ranking, use an endpoint built for it. The dashboard and analytics endpoints — GET /api/v1/dashboards/global, GET /api/v1/analytics/trends, GET /api/v1/repositories/{repositoryId}/risk-summary — return computed rankings and aggregates directly, without paging a collection to reconstruct them.

Combining filters with pagination

Filters and pagination compose. Both are query parameters and both apply to the same request:

bash
curl "https://api.ospulse.app/api/v1/packages?ecosystem=Npm&search=react&take=50&skip=0" \
  -H "Authorization: Bearer $OSPULSE_TOKEN"

The order of operations matters when reading the response:

  1. Filters are applied first, producing the matching set.
  2. skip and take are applied to that filtered set.
  3. totalEstimate reflects the filtered set, not the whole collection.

Never change a filter mid-iteration

skip is an offset into the filtered result. Changing any filter between page requests renumbers every offset, so the pages you have already fetched no longer describe a contiguous window. Fix the filters before the first request and hold them constant until the loop ends.

Filtering early is also the cheapest optimisation available. Narrowing to one ecosystem before paging is far faster than fetching everything and discarding rows in your client — it reduces database work, response size, and the number of round trips at once, and it keeps you away from the deep offsets that make offset pagination slow.

URL-encoding

Filter values go into a query string and must be percent-encoded. Use your HTTP client's query builder — URLSearchParams in JavaScript, the params argument in Python's requests, --data-urlencode with cURL — rather than concatenating strings.

Characters that routinely break hand-built query strings:

CharacterEncodedAppears in
space%20Search terms, product names
@%40Scoped npm package names
/%2FScoped npm names, Go module paths, Maven coordinates
:%3AISO-8601 timestamps, Maven groupId:artifactId
+%2BVersion metadata. Unencoded, + decodes as a space
&, =%26, %3DAny value containing them terminates the parameter early
bash
# Wrong — @ and / are interpreted before the server sees them
curl "https://api.ospulse.app/api/v1/packages?search=@angular/core"
 
# Right — let cURL encode the value
curl -G "https://api.ospulse.app/api/v1/packages" \
  --data-urlencode "search=@angular/core" \
  --data-urlencode "ecosystem=Npm" \
  --data-urlencode "take=25" \
  -H "Authorization: Bearer $OSPULSE_TOKEN"
js
const query = new URLSearchParams({
  search: '@angular/core',
  ecosystem: 'Npm',
  take: 25,
})
 
const res = await fetch(`https://api.ospulse.app/api/v1/packages?${query}`, {
  headers: { Authorization: `Bearer ${process.env.OSPULSE_TOKEN}` },
})

Identifiers need no encoding — a GUID contains only hexadecimal digits and hyphens — but encoding them anyway is harmless and keeps one code path.

Finding the parameters for an endpoint

  1. Open the interactive reference and locate the operation.
  2. Read its Parameters section: name, location (query or path), type, and whether it is required.
  3. For an enum-valued parameter, expand the schema to see the accepted members.
  4. To script against it, read the same information from /openapi.json — the parameters array on each operation is the exact source the reference is rendered from.

Next steps