From 252bd7618773c68b6e0651585c4566543beccb0e Mon Sep 17 00:00:00 2001 From: adityasingh Date: Fri, 9 Oct 2026 14:37:11 +0530 Subject: [PATCH] COBS-1353: Add Insights spans search query endpoint Document POST /v2/insights/query/{region}/spans/search, mirroring the existing logs search endpoint. Adds the operation, request/response models, filter grammar (condition/and/or/not/text_search), ordering, pagination, and a cURL example. Co-authored-by: Cursor --- specification/DigitalOcean-public.v2.yaml | 4 ++ .../curl/insights_post_spansSearch.yml | 33 +++++++++ .../insights/insights_post_spansSearch.yml | 58 +++++++++++++++ .../resources/insights/models/span_record.yml | 70 +++++++++++++++++++ .../models/spans_filter_expression.yml | 45 ++++++++++++ .../insights/models/spans_filter_field.yml | 23 ++++++ .../insights/models/spans_filter_value.yml | 37 ++++++++++ .../insights/models/spans_order_by.yml | 14 ++++ .../models/spans_pagination_request.yml | 13 ++++ .../models/spans_pagination_response.yml | 9 +++ .../insights/models/spans_search_request.yml | 18 +++++ .../insights/models/spans_search_response.yml | 10 +++ .../insights/models/spans_time_bound.yml | 19 +++++ .../insights/models/spans_time_range.yml | 10 +++ .../insights/responses/spans_search.yml | 14 ++++ 15 files changed, 377 insertions(+) create mode 100644 specification/resources/insights/examples/curl/insights_post_spansSearch.yml create mode 100644 specification/resources/insights/insights_post_spansSearch.yml create mode 100644 specification/resources/insights/models/span_record.yml create mode 100644 specification/resources/insights/models/spans_filter_expression.yml create mode 100644 specification/resources/insights/models/spans_filter_field.yml create mode 100644 specification/resources/insights/models/spans_filter_value.yml create mode 100644 specification/resources/insights/models/spans_order_by.yml create mode 100644 specification/resources/insights/models/spans_pagination_request.yml create mode 100644 specification/resources/insights/models/spans_pagination_response.yml create mode 100644 specification/resources/insights/models/spans_search_request.yml create mode 100644 specification/resources/insights/models/spans_search_response.yml create mode 100644 specification/resources/insights/models/spans_time_bound.yml create mode 100644 specification/resources/insights/models/spans_time_range.yml create mode 100644 specification/resources/insights/responses/spans_search.yml diff --git a/specification/DigitalOcean-public.v2.yaml b/specification/DigitalOcean-public.v2.yaml index 51cb1b467..6ac54ab7e 100644 --- a/specification/DigitalOcean-public.v2.yaml +++ b/specification/DigitalOcean-public.v2.yaml @@ -1840,6 +1840,10 @@ paths: post: $ref: "resources/insights/insights_post_logsSearch.yml" + /v2/insights/query/{region}/spans/search: + post: + $ref: "resources/insights/insights_post_spansSearch.yml" + /v1/consent: get: $ref: "resources/signals/signals_list_consents.yml" diff --git a/specification/resources/insights/examples/curl/insights_post_spansSearch.yml b/specification/resources/insights/examples/curl/insights_post_spansSearch.yml new file mode 100644 index 000000000..66c20f1c1 --- /dev/null +++ b/specification/resources/insights/examples/curl/insights_post_spansSearch.yml @@ -0,0 +1,33 @@ +lang: cURL +source: |- + curl -X POST \ + -H "Authorization: Bearer $DIGITALOCEAN_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "timeRange": { + "from": {"relative": "1h"}, + "to": {"relative": "now"} + }, + "filter": { + "type": "and", + "expressions": [ + { + "type": "condition", + "field": {"name": "service.name"}, + "operator": "eq", + "value": {"type": "string", "value": "api-gateway"} + }, + { + "type": "condition", + "field": {"name": "statusCode"}, + "operator": "eq", + "value": {"type": "string", "value": "Error"} + } + ] + }, + "orderBy": [ + {"field": {"name": "startTime"}, "direction": "desc"} + ], + "pagination": {"limit": 100} + }' \ + "https://api.digitalocean.com/v2/insights/query/nyc3/spans/search" diff --git a/specification/resources/insights/insights_post_spansSearch.yml b/specification/resources/insights/insights_post_spansSearch.yml new file mode 100644 index 000000000..9d6b3d60c --- /dev/null +++ b/specification/resources/insights/insights_post_spansSearch.yml @@ -0,0 +1,58 @@ +operationId: insights_post_spansSearch + +summary: Search spans + +description: >- + To search spans in a region, send a POST request to + `/v2/insights/query/{region}/spans/search` with a JSON body describing the + time range, optional filter, ordering, and pagination. + + The time range must not exceed 7 days. `pagination.limit` defaults to 100 and + must be between 1 and 1000. Results are ordered by `startTime` descending by + default. Cursor-based pagination is not yet supported; `pagination.hasMore` + indicates whether additional spans matched beyond the returned page. + +tags: + - Insights + +parameters: + - $ref: 'parameters.yml#/region' + +requestBody: + required: true + content: + application/json: + schema: + $ref: 'models/spans_search_request.yml' + +responses: + '200': + $ref: 'responses/spans_search.yml' + + '400': + $ref: '../../shared/responses/bad_request.yml' + + '401': + $ref: '../../shared/responses/unauthorized.yml' + + '403': + $ref: '../../shared/responses/forbidden.yml' + + '404': + $ref: '../../shared/responses/not_found.yml' + + '429': + $ref: '../../shared/responses/too_many_requests.yml' + + '500': + $ref: '../../shared/responses/server_error.yml' + + default: + $ref: '../../shared/responses/unexpected_error.yml' + +x-codeSamples: + - $ref: 'examples/curl/insights_post_spansSearch.yml' + +security: + - bearer_auth: + - 'insights:read' diff --git a/specification/resources/insights/models/span_record.yml b/specification/resources/insights/models/span_record.yml new file mode 100644 index 000000000..4bccc3a51 --- /dev/null +++ b/specification/resources/insights/models/span_record.yml @@ -0,0 +1,70 @@ +type: object +description: A single span returned by a spans search. +properties: + traceId: + type: string + description: The trace identifier the span belongs to. + example: "4bf92f3577b34da6a3ce929d0e0e4736" + spanId: + type: string + description: The span identifier. + example: "00f067aa0ba902b7" + parentSpanId: + type: string + description: The parent span identifier. Omitted for root spans. + example: "00f067aa0ba902b6" + startTime: + type: string + format: date-time + description: The span start time. + example: "2026-09-30T00:00:00Z" + endTime: + type: string + format: date-time + description: The span end time. + example: "2026-09-30T00:00:01Z" + durationNs: + type: integer + format: int64 + description: The span duration in nanoseconds. + example: 1000000 + name: + type: string + description: The span name. + example: "GET /v2/droplets" + kind: + type: string + description: The span kind. + example: "Server" + statusCode: + type: string + description: The span status code. + example: "Ok" + statusMessage: + type: string + description: The span status message. Omitted when empty. + example: "connection reset by peer" + serviceName: + type: string + description: The name of the service that emitted the span. + example: "api-gateway" + resource: + type: object + description: Resource attributes associated with the span. + additionalProperties: + type: string + example: + service.name: "api-gateway" + service.version: "1.4.2" + attributes: + type: object + description: Span attributes. + additionalProperties: + type: string + example: + http.method: "GET" + http.status_code: "200" + region: + type: string + description: The datacenter region slug the span was queried from. + example: "nyc3" diff --git a/specification/resources/insights/models/spans_filter_expression.yml b/specification/resources/insights/models/spans_filter_expression.yml new file mode 100644 index 000000000..a2f13ee4f --- /dev/null +++ b/specification/resources/insights/models/spans_filter_expression.yml @@ -0,0 +1,45 @@ +type: object +description: >- + A boolean filter tree for spans search. Set `type` to select the node kind: + `condition` compares a single field, `and`/`or`/`not` combine child + expressions, and `text_search` matches a substring across the span name, + service name, and status message. +required: + - type +properties: + type: + type: string + description: The node kind. + enum: + - condition + - and + - or + - not + - text_search + example: condition + field: + $ref: 'spans_filter_field.yml' + operator: + type: string + description: The comparison operator for a `condition` node. + enum: + - eq + - neq + - gt + - gte + - lt + - lte + - in + - exists + example: eq + value: + $ref: 'spans_filter_value.yml' + expressions: + type: array + description: Child expressions for `and`, `or`, and `not` nodes. + items: + $ref: 'spans_filter_expression.yml' + query: + type: string + description: The substring to match for a `text_search` node. + example: "timeout" diff --git a/specification/resources/insights/models/spans_filter_field.yml b/specification/resources/insights/models/spans_filter_field.yml new file mode 100644 index 000000000..252dc32a8 --- /dev/null +++ b/specification/resources/insights/models/spans_filter_field.yml @@ -0,0 +1,23 @@ +type: object +description: A reference to a searchable span field. +required: + - name +properties: + name: + type: string + description: >- + The field name. Well-known span fields include `traceId`, `spanId`, + `parentSpanId`, `startTime`, `endTime`, `durationNs`, `name`, + `serviceName`, `kind`, `statusCode`, and `statusMessage`. Any other value + is treated as a span or resource attribute key. + example: service.name + scope: + type: string + description: >- + Optional attribute scope. Use `resource` to match a resource attribute or + `attributes` to match a span attribute. Omit for well-known fields or to + search both attribute maps. + enum: + - resource + - attributes + example: attributes diff --git a/specification/resources/insights/models/spans_filter_value.yml b/specification/resources/insights/models/spans_filter_value.yml new file mode 100644 index 000000000..9266faf4f --- /dev/null +++ b/specification/resources/insights/models/spans_filter_value.yml @@ -0,0 +1,37 @@ +description: >- + A filter comparison value. Provide either a bare JSON scalar or string array, + or a typed wrapper of the form `{ "type": "string", "value": "..." }`. +example: + type: string + value: "api-gateway" +oneOf: + - type: string + - type: number + - type: boolean + - type: array + items: + type: string + - type: object + description: A typed value wrapper. + required: + - value + properties: + type: + type: string + description: The value type. + enum: + - string + - number + - bool + - string_array + example: string + value: + description: The scalar or array value, matching `type`. + example: "api-gateway" + oneOf: + - type: string + - type: number + - type: boolean + - type: array + items: + type: string diff --git a/specification/resources/insights/models/spans_order_by.yml b/specification/resources/insights/models/spans_order_by.yml new file mode 100644 index 000000000..c22657f61 --- /dev/null +++ b/specification/resources/insights/models/spans_order_by.yml @@ -0,0 +1,14 @@ +type: object +description: A sort clause for spans search results. +required: + - field +properties: + field: + $ref: 'spans_filter_field.yml' + direction: + type: string + description: The sort direction. Defaults to `desc` when omitted. + enum: + - asc + - desc + example: desc diff --git a/specification/resources/insights/models/spans_pagination_request.yml b/specification/resources/insights/models/spans_pagination_request.yml new file mode 100644 index 000000000..8648bdac2 --- /dev/null +++ b/specification/resources/insights/models/spans_pagination_request.yml @@ -0,0 +1,13 @@ +type: object +description: Limits the number of spans returned. +properties: + limit: + type: integer + format: int32 + description: >- + Maximum number of spans to return. Defaults to 100 and must be between 1 + and 1000. + default: 100 + minimum: 1 + maximum: 1000 + example: 100 diff --git a/specification/resources/insights/models/spans_pagination_response.yml b/specification/resources/insights/models/spans_pagination_response.yml new file mode 100644 index 000000000..c42b905c8 --- /dev/null +++ b/specification/resources/insights/models/spans_pagination_response.yml @@ -0,0 +1,9 @@ +type: object +description: Pagination metadata for a spans search response. +properties: + hasMore: + type: boolean + description: >- + Whether more spans matched than were returned in this page. Cursor-based + continuation is not yet supported. + example: true diff --git a/specification/resources/insights/models/spans_search_request.yml b/specification/resources/insights/models/spans_search_request.yml new file mode 100644 index 000000000..818c9b8cf --- /dev/null +++ b/specification/resources/insights/models/spans_search_request.yml @@ -0,0 +1,18 @@ +type: object +description: Request body for searching spans. +required: + - timeRange +properties: + timeRange: + $ref: 'spans_time_range.yml' + filter: + $ref: 'spans_filter_expression.yml' + orderBy: + type: array + description: >- + Sort clauses applied to the result set. Defaults to `startTime` + descending when omitted. + items: + $ref: 'spans_order_by.yml' + pagination: + $ref: 'spans_pagination_request.yml' diff --git a/specification/resources/insights/models/spans_search_response.yml b/specification/resources/insights/models/spans_search_response.yml new file mode 100644 index 000000000..81b60e108 --- /dev/null +++ b/specification/resources/insights/models/spans_search_response.yml @@ -0,0 +1,10 @@ +type: object +description: Response body for a spans search. +properties: + data: + type: array + description: Matching spans. Empty when no spans match. + items: + $ref: 'span_record.yml' + pagination: + $ref: 'spans_pagination_response.yml' diff --git a/specification/resources/insights/models/spans_time_bound.yml b/specification/resources/insights/models/spans_time_bound.yml new file mode 100644 index 000000000..aeb92428b --- /dev/null +++ b/specification/resources/insights/models/spans_time_bound.yml @@ -0,0 +1,19 @@ +type: object +description: A single time bound. Exactly one of `relative` or `absolute` is set. +minProperties: 1 +maxProperties: 1 +properties: + relative: + type: string + description: >- + A relative time. Use `now` for the current time, a bare duration such as + `1h` or `7d` to look back from now, or an offset such as `now-15m` or + `now+30m`. Supported units are `s`, `m`, `h`, `d`, and `w`. + example: "1h" + absolute: + type: string + description: >- + An absolute timestamp. Accepts an RFC3339/RFC3339Nano string or a Unix + timestamp (seconds, milliseconds, microseconds, or nanoseconds) encoded as + a decimal string. + example: "2026-09-30T00:00:00Z" diff --git a/specification/resources/insights/models/spans_time_range.yml b/specification/resources/insights/models/spans_time_range.yml new file mode 100644 index 000000000..62aa9e26d --- /dev/null +++ b/specification/resources/insights/models/spans_time_range.yml @@ -0,0 +1,10 @@ +type: object +description: An inclusive query time window. The window must not exceed 7 days. +required: + - from + - to +properties: + from: + $ref: 'spans_time_bound.yml' + to: + $ref: 'spans_time_bound.yml' diff --git a/specification/resources/insights/responses/spans_search.yml b/specification/resources/insights/responses/spans_search.yml new file mode 100644 index 000000000..20ee2a7ad --- /dev/null +++ b/specification/resources/insights/responses/spans_search.yml @@ -0,0 +1,14 @@ +description: Spans search result. + +headers: + ratelimit-limit: + $ref: '../../../shared/headers.yml#/ratelimit-limit' + ratelimit-remaining: + $ref: '../../../shared/headers.yml#/ratelimit-remaining' + ratelimit-reset: + $ref: '../../../shared/headers.yml#/ratelimit-reset' + +content: + application/json: + schema: + $ref: '../models/spans_search_response.yml'