How to use the DORA API to send deployment activity to Span
Last updated: February 20, 2026
The deployments API allows you to track deployment activity and power critical DORA metrics like deployment frequency, lead time to change, and change failure rate.
Follow the steps below to add a step in your deployment workflow to send this data to Span:
Authentication
Personal Access Token (Beta)
A personal access token may be used. When a personal access token is provided, it assumes the permissions of the person associated with it.
Insufficient Permissions
If the credential provided does not have permission to access the catalog data, the response will include a status of 403 Forbidden. For example, Personal Access Tokens assume the permission levels of the person associated with it, which may not include the permission to access the catalog data.
Invalid Credentials
If the request does not include valid credentials, the response will include a status of 401 Unauthorized .
Additional Request Headers
X-Request-ID
All requests accept the optional header X-Request-ID . If this header is not set, Span will generate a unique request id. In all cases, the response will include the chosen request id in the X-Request-ID header.
Idempotency-Key
Some requests may optionally include the Idempotency-Key header, ensuring requests will only be processed once within a window of 24 hours.
In the case of a collision, the response from the last successful request will be returned as-is with a single additional header X-Replayed-Request: true identifying that the request has been replayed and the response is a cached version of the original.
Results will only be saved if the request is successfully executed.
Idempotency keys are scoped to the path and method being requested. The same key may be used across different routes or different methods.
POSTandPATCHrequest methods accept idempotency keys by default.All other methods (e.g. safe methods such as
GETor idempotent methods such asDELETEorPUT) will ignore any idempotency key provided.
Pagination
The catalog api will implement cursor pagination for all list endpoints. A cursor is an opaque string identifying a position in a consistently ordered list of all objects of the same type. In some cases, the cursor may represent the id of a single object, while in others it may be a composite string representing multiple properties of a single object.
When paginating with cursor pagination, you can provide an object’s cursor to either the before or after query parameters. When before is set, you’ll receive the list of objects that are ordered directly before the position of the object represented by the cursor. When after is set, you’ll receive the list of objects that are ordered directly after the position of the object represented by the cursor. In both cases, the limit dictates how many objects to include starting from that position. If both after and before query parameters have been set, the result will be empty.
The following query parameters are supported on list api endpoints:
ParameterDescription | |
| A cursor to user in pagination. This defines the end of the page requested. The result will include objects up to |
| A cursor to user in pagination. This defines the start of the page requested. The result will include objects up to |
| The number of results to include. When |
Paginated responses will include the current page details in the meta.page property of the response’s json data. The structure of the page object is as follows:
ParameterDescription | |
| An opaque string representing the cursor of last record of the current page. |
| An opaque string representing the cursor the first record of the current page. |
| A boolean indicating whether more results exist following the current result set. |
| A boolean indicating whether more results exist preceding the current result set. |
Deployments
Add Deployment
ParameterRequiredDescription | ||
| required | Simple descriptive name for this deploy. |
| optional | Expanded description for this deploy. |
| required | ISO-8601 UTC timestamp representing the time when the deploy was triggered. |
| optional | ISO-8601 UTC timestamp representing the time when the deploy finished. Defaults to the current time if |
| optional | One of |
| optional | The state of this deploy. One of |
| optional | A simple string representing the environment, e.g. |
| optional | A simple string representing the version deployed, e.g. a commit sha or semantic version number |
| optional | A url linking to the external deployment. |
| optional | An array of Span services slug identifiers associated with this deploy. |
| optional | An object of deployer details associated with this deploy. |
| required | The name of the individual who triggered the deploy. |
| required | The email associated with the individual who triggered the deploy. |
| optional | An object of git details associated with this deploy. |
| required | The url of the git repository associated with this deploy. |
| required | The git ref name associated with this deploy, e.g. a branch name, tag name, or commit sha. |
| optional | Any additional data to be included with this deployment. This can be any valid json object. |
| optional | If using a monorepo or a partial deploy flow, this should be a set of the pull request numbers that are being part of this deployment. |
curl -X POST \\
<https://span.app/api/dora/v1/deployments> \\
-H "Authorization: Bearer <access token>" \\
-H "Content-Type: application/json" \\
-H "Idempotency-Key: <optional idempotency key>" \\
-d '{
"title": "Deployment 12345",
"description": "optional deployment description",
"triggeredAt": "2024-03-25T18:00:00Z",
"completedAt": "2024-03-25T18:10:00Z",
"type": "deploy",
"status": "success",
"environment": "production",
"version": "2fd4e1c",
"services": ["api", "web"],
"deployer": {
"email": "first.last@example.com",
"name": "First Last"
},
"git": {
"repoUrl": "<https://github.com/example/repository.git>",
"refName": "2fd4e1c67a2d28fced849ee1bb76e7391b93eb12"
},
"pullRequests": [101, 102, 103],
"metadata": {"anything": "goes"}
}'
Response (201 Created)
{
"meta": {
"cursor": "..."
},
"data": {
"id": "...",
"description": "optional deployment description",
"triggeredAt": "2024-03-25T18:00:00Z",
"completedAt": "2024-03-25T18:10:00Z",
"type": "deploy",
"status": "success",
"environment": "production",
"version": "2fd4e1c",
"httpUrl": null,
"services": ["api", "web"], "deployer": {
"email": "first.last@example.com",
"name": "First Last"
},
"git": {
"repoUrl": "<https://github.com/example/repository.git>",
"refName": "2fd4e1c67a2d28fced849ee1bb76e7391b93eb12"
},
"metadata": {"anything": "goes"}
}
}
Get Deployment
curl <https://span.app/api/dora/v1/deployments/:deploymentId> \\
-H "Authorization: Bearer <access token>"
Response (200 OK)
{
"meta": {
"cursor": "..."
},
"data": {
"id": "...",
"description": "optional deployment description",
"triggeredAt": "2024-03-25T18:00:00Z",
"completedAt": "2024-03-25T18:10:00Z",
"type": "deploy",
"status": "success",
"environment": "production",
"version": "2fd4e1c",
"httpUrl": null,
"services": ["api", "web"],
"deployer": {
"email": "first.last@example.com",
"name": "First Last"
},
"git": {
"repoUrl": "<https://github.com/example/repository.git>",
"refName": "2fd4e1c67a2d28fced849ee1bb76e7391b93eb12"
},
"metadata": {"anything": "goes"}
}
}
List Deployments
curl <https://span.app/api/dora/v1/deployments> \\
-H "Authorization: Bearer <access token>"
Response (200 OK)
{
"meta": {
"page": {
"endCursor": "...",
"startCursor": null,
"hasNextPage": true,
"hasPreviousPage": false
},
},
"data": [
{
"id": "...",
"description": "optional deployment description",
"triggeredAt": "2024-03-25T18:00:00Z",
"completedAt": "2024-03-25T18:10:00Z",
"type": "deploy",
"status": "success",
"environment": "production",
"version": "2fd4e1c",
"httpUrl": null,
"services": ["api", "web"],
"deployer": {
"email": "first.last@example.com",
"name": "First Last"
},
"git": {
"repoUrl": "<https://github.com/example/repository.git>",
"refName": "2fd4e1c67a2d28fced849ee1bb76e7391b93eb12"
},
"metadata": {"anything": "goes"}
},
...
]
}
Update Deployment
Deployments may be updated with a PATCH /api/dora/v1/deployments/:deploymentId request. Any parameter containing a null value will unset its value. Parameters not included with the request will remain unchanged.
If status transitions from pending to either success or failure, completedAt will be updated to reflect the time of the request unless it is specified in the update.
ParameterRequiredNullableDescription | |||
| optional | false | Simple descriptive name for this deploy. |
| optional | true | Expanded description for this deploy. |
| required | false | ISO-8601 UTC timestamp representing the time when the deploy was triggered. |
| optional | true | ISO-8601 UTC timestamp representing the time when the deploy finished. Defaults to the current time if |
| optional | false | One of |
| optional | false | The state of this deploy. One of |
| optional | true | A simple string representing the environment, e.g. |
| optional | true | A simple string representing the version deployed, e.g. a commit sha or semantic version number |
| optional | true | A url linking to the external deployment. |
| optional | false | An array of Span services slug identifiers associated with this deploy. |
| optional | true | An object of deployer details associated with this deploy. |
| required | false | The name of the individual who triggered the deploy. |
| required | false | The email associated with the individual who triggered the deploy. |
| optional | true | An object of git details associated with this deploy. |
| required | false | The url of the git repository associated with this deploy. |
| required | false | The git ref name associated with this deploy, e.g. a branch name, tag name, or commit sha. |
| optional | false | Any additional data to be included with this deployment. This can be any valid json object. This object will be replaced on updates. |
curl -X PATCH \\
<https://span.app/api/dora/v1/deployments/:deploymentId> \\
-H "Authorization: Bearer <access token>" \\
-H "Content-Type: application/json" \\
-d '{
"status": "failure"
}'
Response (200 OK)
{
"meta": {
"cursor": "..."
},
"data": {
"id": "...",
"description": "optional deployment description",
"triggeredAt": "2024-03-25T18:00:00Z",
"completedAt": "2024-03-25T18:10:00Z",
"type": "deploy",
"status": "failure",
"environment": "production",
"version": "2fd4e1c",
"httpUrl": null,
"services": ["api", "web"],
"deployer": {
"email": "first.last@example.com",
"name": "First Last"
},
"git": {
"repoUrl": "<https://github.com/example/repository.git>",
"refName": "2fd4e1c67a2d28fced849ee1bb76e7391b93eb12"
},
"metadata": {"anything": "goes"}
}
}
Delete Deployment
curl -X DELETE \\
<https://span.app/api/dora/v1/deployments/:deploymentId> \\
-H "Authorization: Bearer <access token>"
Response (204 No Content)
Ownership
If the services property is defined on the deployment record, ownership will be inferred from the associated services based on their owners definition. For example, let’s assume we have a service api defined as follows:
{
"id": "...",
"name": "API Service",
"slug": "api",
"description": "api",
"owners": [
{
"type": "team",
"slug": "team-a"
}
],
"git": {
"repoUrl": "<https://github.com/example/repository.git>"
},
"metadata": {"anything": "goes"}
}
If the deployment lists api as one of the associated services, the deployment will be linked to team-a and will be counted in team-a metrics. If no owners are listed for the associated services or if no services were associated with the deployment, then the deployment will remain unlinked but be counted in the organization-level metrics.
Incidents
Add Incident
ParameterRequiredDescription | ||
| required | Simple descriptive name for this incident. |
| optional | Expanded description for this incident. |
| optional | The severity of this incident, a number in the range 0-3, with 0 representing the most critical severity level. |
| required | ISO-8601 UTC timestamp representing the time when the incident was declared. |
| optional | ISO-8601 UTC timestamp representing the time when the work to resolve the incident began. |
| optional | ISO-8601 UTC timestamp representing the time when the incident was successfully resolved. |
| optional | A url linking to the external incident. |
| optional | A simple string representing the environment, e.g. |
| optional | An array of Span services slug identifiers associated with this incident. |
| optional | An array of Span owner identifiers. |
| required | The type of owner. Only |
| required | The Span slug of the owning team. |
| optional | An object of git details responsible for this incident. |
| required | The url of the git repository associated with this incident. |
| required | The git ref name responsible for this incident, e.g. a branch name, tag name, or commit sha. |
| optional | An array of deployment ids that were responsible for the incident. |
| optional | An array of deployment ids that were responsible for resolving the incident. |
| optional | Any additional data to be included with this incident. This can be any valid json object. |
curl -X POST \\
<https://span.app/api/dora/v1/incidents> \\
-H "Authorization: Bearer <access token>" \\
-H "Content-Type: application/json" \\
-H "Idempotency-Key: <optional idempotency key>" \\
-d '{
"title": "Incident 12345",
"severity": 1,
"issuedAt": "2024-03-25T18:00:00Z",
"metadata": {"anything": "goes"}
}'
Response (201 Created)
{
"meta": {
"cursor": "..."
},
"data": {
"id": "...",
"title": "Incident 12345",
"description": null,
"severity": 1,
"issuedAt": "2024-03-25T18:00:00Z",
"startedAt": null,
"endedAt": null,
"httpUrl": null,
"services": [],
"owners": [],
"git": null,
"triggeringDeployments": [],
"resolvingDeployments": [],
"metadata": {"anything": "goes"}
}
}
Get Incident
curl <https://span.app/api/dora/v1/incidents/:incidentId> \\
-H "Authorization: Bearer <access token>"
Response (200 OK)
{
"meta": {
"cursor": "..."
},
"data": {
"id": "...",
"title": "Incident 12345",
"description": null,
"severity": 1,
"issuedAt": "2024-03-25T18:00:00Z",
"startedAt": null,
"endedAt": null,
"httpUrl": null,
"services": [],
"owners": [],
"git": null,
"triggeringDeployments": [],
"resolvingDeployments": [],
"metadata": {"anything": "goes"}
}
}
List Incidents
curl <https://span.app/api/dora/v1/incidents> \\
-H "Authorization: Bearer <access token>"
Response (200 OK)
{
"meta": {
"page": {
"endCursor": "...",
"startCursor": null,
"hasNextPage": true,
"hasPreviousPage": false
}
},
"data": [
{
"id": "...",
"title": "Incident 12345",
"description": null,
"severity": 1,
"issuedAt": "2024-03-25T18:00:00Z",
"startedAt": null,
"endedAt": null,
"httpUrl": null,
"services": [],
"owners": [],
"git": null,
"triggeringDeployments": [],
"resolvingDeployments": [],
"metadata": {"anything": "goes"}
},
...
]
}
Update Incident
Incidents may be updated with a PATCH /api/dora/v1/incidents/:incidentId request. Any parameter containing a null value will unset its value. Parameters not included with the request will remain unchanged.
ParameterRequiredNullableDescription | |||
| optional | false | Simple descriptive name for this incident. |
| optional | true | Expanded description for this incident. |
| optional | true | The severity of this incident, a number in the range 0-3, with 0 representing the most critical severity level. |
| optional | false | ISO-8601 UTC timestamp representing the time when the incident was declared. |
| optional | true | ISO-8601 UTC timestamp representing the time when the work to resolve the incident began. |
| optional | true | ISO-8601 UTC timestamp representing the time when the incident was successfully resolved. |
| optional | true | A url linking to the external incident. |
| optional | true | A simple string representing the environment, e.g. |
| optional | false | An array of Span services slug identifiers associated with this incident. |
| optional | false | An array of Span owner identifiers. |
| required | false | The type of owner. Only |
| required | false | The Span slug of the owning team. |
| optional | true | An object of git details responsible for this incident. |
| required | false | The url of the git repository associated with this incident. |
| required | false | The git ref name responsible for this incident, e.g. a branch name, tag name, or commit sha. |
| optional | false | An array of deployment ids that were responsible for the incident. |
| optional | false | An array of deployment ids that were responsible for resolving the incident. |
| optional | false | Any additional data to be included with this incident. This can be any valid json object. |
curl -X PATCH \\
<https://span.app/api/dora/v1/incidents/:incidentId> \\
-H "Authorization: Bearer <access token>" \\
-H "Content-Type: application/json" \\
-d '{
"startedAt": "2024-03-25T19:00:00Z"
}'
Response (200 OK)
{
"meta": {
"cursor": "..."
},
"data": {
"id": "...",
"title": "Incident 12345",
"description": null,
"severity": 1,
"issuedAt": "2024-03-25T18:00:00Z",
"startedAt": "2024-03-25T19:00:00Z",
"endedAt": null,
"httpUrl": null,
"services": [],
"owners": [],
"git": null,
"triggeringDeployments": [],
"resolvingDeployments": [],
"metadata": {"anything": "goes"}
}
}
Delete Incident
curl -X DELETE \\
<https://span.app/api/dora/v1/incidents/:incidentId> \\
-H "Authorization: Bearer <access token>"
Response (204 No Content)
Ownership
If the owners property is set, the incident will be counted in metrics for all teams explicitly listed. Otherwise, the incident will be counted in metrics at the organization-level and remain unlinked from any specific team.
Services
Add Service
ParameterRequiredDescription | ||
| required | Simple descriptive name for this service. |
| optional | A unique slug representing this service. If one isn’t provided, it will be generated from the |
| optional | Expanded description for this service. |
| optional | An array of Span owner identifiers. |
| required | The type of owner. Only |
| required | The Span slug of the owning team. |
| optional | An object of git details associated with this service. |
| required | The url of the git repository associated with this service. |
| optional | Any additional data to be included with this service. This can be any valid json object. |
curl -X POST \\
<https://span.app/api/dora/v1/services> \\
-H "Authorization: Bearer <access token>" \\
-H "Content-Type: application/json" \\
-H "Idempotency-Key: <optional idempotency key>" \\
-d '{
"name": "Service A",
"slug": "service-a",
"description": "optional service description",
"owners": [
{
"type": "team",
"slug": "team-a"
}
],
"git": {
"repoUrl": "<https://github.com/example/repository.git>"
},
"metadata": {"anything": "goes"}
}'
Response (201 Created)
{
"meta": {
"cursor": "..."
},
"data": {
"id": "...",
"slug": "service-a",
"title": "Service A",
"description": "optional service description",
"owners": [
{
"type": "team",
"slug": "team-a"
}
],
"git": {
"repoUrl": "<https://github.com/example/repository.git>"
},
"metadata": {"anything": "goes"}
}
}
Get Service
curl <https://span.app/api/dora/v1/services/:serviceId> \\
-H "Authorization: Bearer <access token>"
Response (200 OK)
{
"meta": {
"cursor": "..."
},
"data": {
"id": "...",
"name": "Service A",
"slug": "service-a",
"description": "optional service description",
"owners": [
{
"type": "team",
"slug": "team-a"
}
],
"git": {
"repoUrl": "<https://github.com/example/repository.git>"
},
"metadata": {"anything": "goes"}
}
}
List Services
curl <https://span.app/api/dora/v1/services> \\
-H "Authorization: Bearer <access token>"
Response (200 OK)
{
"meta": {
"page": {
"endCursor": "...",
"startCursor": null,
"hasNextPage": true,
"hasPreviousPage": false
},
},
"data": [
{
"id": "...",
"name": "Service A",
"slug": "service-a",
"description": "optional service description",
"owners": [
{
"type": "team",
"slug": "team-a"
}
],
"git": {
"repoUrl": "<https://github.com/example/repository.git>"
},
"metadata": {"anything": "goes"}
},
...
]
}
Update Service
Services may be updated with a PATCH /api/dora/v1/services/:serviceId request. Any parameter containing a null value will unset its value. Parameters not included with the request will remain unchanged.
ParameterRequiredNullableDescription | |||
| optional | false | Simple descriptive name for this service. |
| optional | false | A unique slug representing this service. If one isn’t provided, it will be generated from the |
| optional | true | Expanded description for this service. |
| optional | false | An array of Span owner identifiers. |
| required | false | The type of owner. Only |
| required | false | The Span slug of the owning team. |
| optional | true | An object of git details associated with this service. |
| required | false | The url of the git repository associated with this service. |
| optional | false | Any additional data to be included with this service. This can be any valid json object. This object will be replaced on updates. |
curl -X PATCH \\
<https://span.app/api/dora/v1/services/:serviceId> \\
-H "Authorization: Bearer <access token>" \\
-H "Content-Type: application/json" \\
-d '{
"description": "api documentation"
}'
Response (200 OK)
{
"meta": {
"cursor": "..."
},
"data": {
"id": "...",
"name": "Service A",
"slug": "service-a",
"description": "api documentation",
"owners": [
{
"type": "team",
"slug": "team-a"
}
],
"git": {
"repoUrl": "<https://github.com/example/repository.git>"
},
"metadata": {"anything": "goes"}
}
}
Delete Service
curl -X DELETE \\
<https://span.app/api/dora/v1/services/:serviceId> \\
-H "Authorization: Bearer <access token>"
Response (204 No Content)
Ownership: Connecting deployments to teams
Ownership determines which team's DORA metrics a deployment contributes to. There are three options:
Option 1: Services array (recommended)
> "services": ["payments-api", "checkout-worker"]
> Span resolves ownership via the service's team owner. If a slug doesn't exist, Span auto-creates the service — but without a team owner. A Span admin must set ownership via Catalog → Services → [service] → Owner. Until this is done, deployments fall back to root org attribution.
Support tip: "Deployments visible in catalog but not under my team" almost always means missing service-to-team ownership.
Option 2: Explicit PR linking
Provide the pullRequests array (PR numbers) alongside git.repoUrl. Ownership is then inferred from the PRs' authors and their team membership.
Option 3: Automatic PR linking (on request)
Span can automatically link deployments to PRs using the git.refName commit SHA — no pullRequests array required. This must be enabled by Span support on a per-org basis.
FAQ:
When Will my deployment appear in Span?
Deployments are stored immediately after a successful API call. Enrichment (PR links, lead time) happens asynchronously — expect full visibility in DORA metric pages within approximately 3 hours. The deployment will appear in the catalog right away, but metric pages have additional requirements (see below).
What are the requirements for metrics to show data?
Status must be success
Only "status": "success" deployments count. pending and failure are excluded from all metric calculations.
Environment must be production
Only environments whose name is null (omitted) or contains prod or prd (case-insensitive) are counted:
-✅ "production", "prod", "PRD-us-east-1", omitted
- ❌ "staging", "dev", "test", "qa", "pre-prod"
Note: "pre-prod" does not match — the string must contain prod or prd.