deployments.create
Create a new deployment.
This method only creates records and rejects an existing reference_id. Use deployments.upsert for idempotent create-or-update ingestion, or deployments.update to patch an existing deployment without creating one.
Facts
| Method | POST https://yourinstance.getdx.net/api/deployments.create |
Arguments
Required arguments
| Name | Type | Description |
|---|---|---|
token |
Token |
Auth token passed as an HTTP header. |
deployed_at |
Timestamp |
Timestamp of the deployment. DX accepts an ISO-8601 timestamp, Unix seconds, or Unix milliseconds. Use a full ISO-8601 timestamp when sending CSV or JSON data manually. Example: 2025-01-01T12:05:00Z |
service |
Object or Text |
Details of the service that the code was deployed to. Send an object with identifier and optional name, or send a string to use the same value for both fields. |
repository or merge_commit_shas |
Text or Array<Text> |
Provide either a recognized repository or the merge commit SHAs included in the deployment. |
Recommended attribution arguments
For DORA lead-time metrics, DX recommends sending one of these patterns with every production deployment:
repositoryandcommit_shatogether, when a single deployed SHA can anchor attribution.merge_commit_shas, when the deployment system already knows the exact merge commits that shipped.repositoryis not required for this path.
Without commit_sha or merge_commit_shas, DX falls back to deployed_at timestamps, which is less precise. Sending repository without a SHA still attributes undeployed pull requests on the repository default branch. See Attribution paths.
If you send commit_sha or merge_commit_shas, include a valid value. commit_sha must be 7—40 characters. Each SHA in merge_commit_shas must be 7—40 characters. Omit the field if you do not have a value. Do not send an empty string.
If attributing deployments to code via a single commit SHA:
| Name | Type | Description |
|---|---|---|
repository |
Text |
Repository name associated with the deployment—including the organization prefix. Example: myorg/payments*For GitLab, use the project name alone (no org prefix), or the project’s full namespace path if more than one project shares that name, e.g. group/subgroup/payments.*For Bitbucket Data Center, prefix with the project key or project name, project_key/repository_name*For ADO, prefix repo name with the project name instead of organization. *For Gerrit, include only the project name. |
commit_sha |
Text |
Commit SHA deployed to production. Length must be between 7—40 chars. If SHA is not provided, deployment attribution will be done by deployed_at timestamps which is less accurate. Example: a0e61dfff93b2b07788cacf2c1cb3948ed4a39b2 |
If attributing deployments to specific set of commit SHAs:
| Name | Type | Description |
|---|---|---|
merge_commit_shas |
Array<Text> |
A JSON array of commit SHA strings being deployed, used for attributing deploys to pull requests. Each commit SHA should be 7—40 chars. Do not send an object or brace-delimited list such as {"sha_1", "sha_2"}.Example: ["a0e61dfff93b", "07788cacf2c"] |
Optional arguments
| Name | Type | Description |
|---|---|---|
reference_id |
Text or Integer |
Unique identifier for the deployment. If omitted, DX generates a random UUID. A duplicate value returns reference_id_exists; use deployments.upsert when retries should create or update the same deployment.Example: release-v2.4.0 |
source_url |
Text |
An external URL containing more information about your deployment. This will be displayed in the UI when exploring your data. Example: https://my_argocd.myplace.org/build/1791 |
source_name |
Text |
The source for the deployment to help you differentiate and compare data generated from different types of deploy systems. Example: argoCD |
metadata |
JSON |
You may pass a metadata object with additional data about your deployment that you define. Example: { "version": "v1.11.23" } |
environment |
Text |
Deployment environment. Defaults to production when omitted.Example: production |
integration_branch |
String |
Set this value if you are using a branch to integrate changes before merging to a release branch. Example: develop |
success |
Boolean |
Whether the deployment was successful. Default is true if not provided. Send this as a JSON boolean, not a string.Example: true |
Usage info
Deployments API methods allow creating deployments in DX from any data source. API-created deployments offer a flexible approach that can handle a wide range of deployment mechanisms.
For DORA metrics, the API-required fields are not always enough. DX also needs enough attribution data to know which production deployment shipped which pull requests or commits, and which service was deployed.
In most workflows, call deployments.create when a CI/CD pipeline completes a production deployment. If deployment events are aggregated or stored elsewhere, you can batch API calls and submit deployment data to DX on a regular schedule.
Choosing a deployment method
- Use
deployments.createwhen every request represents a new deployment and duplicate identifiers should fail. - Use
deployments.upsertwhen requests should safely create or patch a deployment. - Use
deployments.updatewhen only existing deployments should be patched. - Use
deployments.deleteto permanently remove an API-created deployment.
DORA attribution fields
Use these fields when the deployment should contribute to DORA reporting:
| Field | Format | Use |
|---|---|---|
deployed_at |
ISO-8601 timestamp string, Unix seconds, or Unix milliseconds. Use a full ISO-8601 timestamp such as 2025-01-01T12:05:00Z when sending CSV or JSON data manually. |
Tells DX when production changed. |
service.identifier |
Nested inside the service object, such as "service": {"identifier": "payments-service"}. The API also accepts service as a string. |
Tells DX which service was deployed. Use the same identifier across deployments, incidents, and catalog services. |
repository |
Text string with the organization, project, or source-control prefix expected by your connector, such as myorg/payments. |
Tells DX which repository contains the deployed code. Include the organization or project prefix expected by your source control connector. |
commit_sha |
Text string between 7 and 40 characters, such as a0e61dfff93b. |
Tells DX which commit reached production. Use this when a single deployed SHA can anchor attribution. |
merge_commit_shas |
JSON array of SHA strings, such as ["a0e61dfff93b", "07788cacf2c"]. Do not send {"sha_1", "sha_2"}. |
Tells DX exactly which merged pull requests shipped. Use this when your deployment system already knows the included merge commits. |
integration_branch |
Text string, such as develop. |
Tells DX which branch to use for attribution when pull requests merge into an integration branch before release. |
If you send success, omit it for successful deployments or send a JSON boolean such as true or false. Do not send string values such as "true".
For monorepos where a pull request can affect multiple services, use deployments.setPullServices with this method so DX can attribute pull requests to the right service deployments.
Attribution paths
DX supports several paths to attributing deployments to code changes:
-
When only the
repositoryargument is provided, the deployment is attributed to all non-deployed pull requests in the specified repository with base refs equal to the default branch of the repository. -
When the
commit_shaandrepositoryarguments are provided, the deployment is attributed to the matching pull request as well as all previous non-deployed pull requests with the same repository and base ref as the matching pull request. -
When the
commit_sha,repository, andintegration_brancharguments are provided, the deployment is attributed only to pull requests merged into theintegration_branch. The time range used to identify pull requests comes from the pull request matching the SHA, and the one from the previous deployment. -
When the
merge_commit_shasargument is provided, the deployment is attributed to the specific pull requests that correspond to the provided SHAs.
For scenarios 2 and 3 described above, “previous deployment” is defined as the most recent deployment using deployed_at that has the same environment, repository, and service as the deployment sent in. DX attributes deployments to historical pull requests to fill in presumed gaps. For example, if given the commits and deployments shown below, commits A and B are attributed to Deployment 1, and commits C, D, and E are attributed to Deployment 2.
Commit Deployment Id Deploy Timestamp
------ ------------- ----------------
A
B 1 2024-03-01
C
D
E 2 2024-06-01
Attribution is attempted immediately. Since there is no guarantee that the related repository data has been imported yet, attribution will be retried until relevant data is found for up to three days.
Backfilling historical deployments
You can backfill historical deployments by providing a CSV sheet containing the same fields as used for the deployments.create endpoint. Each row in the spreadsheet represents one deployment. Once you the CSV ready, contact your DX account representative to get your CSV imported.
Example request
This is a typical request:
curl -X POST https://yourinstance.getdx.net/api/deployments.create \
-H "Content-Type: application/json" \
-H "Authorization: Bearer xxxx-xxxxxxxxx-xxxx" \
--data '{
"reference_id": "release-v2.4.0",
"deployed_at": "2025-01-01T12:05:00Z",
"service": {
"name": "Payments",
"identifier": "payments-service"
},
"commit_sha": "d1a34f0",
"repository": "my_org/payment_api"
}'
Deployment examples by workflow
GitHub Action
Below is an example of a GitHub Actions workflow that registers deployments after pull requests are merged.
name: Register deploy in DX Data Cloud
on:
pull_request:
types:
- closed
jobs:
register_deploy:
runs-on: ubuntu-latest
env:
API_TOKEN: ${{ secrets.DX_DEPLOYMENT_API_TOKEN }}
DATACLOUD_HOST: ${{ secrets.DX_DATACLOUD_HOST }}
steps:
- name: Register deploy
if: github.event.pull_request.merged == true && github.event.pull_request.base.ref == 'main' # Change 'main' to your base branch name
run: |
sha=$(git rev-parse HEAD)
ts=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
repo=${{ github.repository }}
# This is optional - drop from curl if not used
service="payments-service"
json_payload=$(jq -n \
--arg service "$service" \
--arg repo "$repo" \
--arg sha "$sha" \
--arg ts "$ts" \
'{
service: {identifier: $service},
repository: $repo,
commit_sha: $sha,
deployed_at: $ts
}')
RESPONSE_CODE=$(curl \
"https://${DATACLOUD_HOST}/api/deployments.create"\
-s -o response.txt\
-w "%{http_code}" -X POST \
-H "Authorization: Bearer ${API_TOKEN}" \
-H "Content-Type: application/json" \
-d "$json_payload"
)
if [ "$RESPONSE" -ne 200 ]; then
ERROR_MESSAGE=$(jq -r '.message' response.txt)
ERROR_TYPE=$(jq -r '.error' response.txt)
echo "Error response from API: HTTP status code $RESPONSE"
echo "Error type: $ERROR_TYPE"
echo "Error message: $ERROR_MESSAGE"
exit 1 # Fail the step explicitly if an error is detected
fi
Argo CD
If you deploy with Argo CD, Argo CD Notifications can call deployments.create whenever an Application finishes a successful sync, with no separate CI job. This takes four keys in the argocd-notifications-cm ConfigMap: a webhook service that holds the host and auth header, a template that builds the request body, a trigger that decides when to fire, and a subscription that wires the two together.
Store the API token in argocd-notifications-secret instead of the ConfigMap. Notification services resolve $key references against that secret at send time:
kubectl -n argocd patch secret argocd-notifications-secret --type=merge \
-p '{"stringData":{"dx-api-key":"xxxx-xxxxxxxxx-xxxx"}}'
Then add the four keys to the ConfigMap:
apiVersion: v1
kind: ConfigMap
metadata:
name: argocd-notifications-cm
namespace: argocd
data:
# Holds the host and auth header. `path` in the template below is appended to
# this url.
service.webhook.dx: |
url: https://yourinstance.getdx.net
headers:
- name: Authorization
value: Bearer $dx-api-key
- name: Content-Type
value: application/json
template.dx-deployment: |
webhook:
dx:
method: POST
path: /api/deployments.create
body: |
{
"service": {"identifier": "{{ .app.metadata.name }}"},
"deployed_at": "{{ .app.status.operationState.finishedAt }}",
"reference_id": "argocd/{{ .app.metadata.uid }}/{{ .app.status.operationState.startedAt }}",
"repository": "orgname/{{ .app.metadata.name }}",
"commit_sha": "{{ .app.status.sync.revision }}",
"source_name": "Argo CD",
"source_url": "{{ .context.argocdUrl }}/applications/{{ .app.metadata.name }}",
"metadata": {
"argocd_application": "{{ .app.metadata.name }}",
"argocd_cluster": "{{ .app.spec.destination.server }}",
"argocd_namespace": "{{ .app.spec.destination.namespace }}",
"argocd_sync_status": "{{ .app.status.sync.status }}",
"argocd_health_status": "{{ .app.status.health.status }}"
}
}
# Fires only on syncs that actually rolled out. Change the destination check to
# match your production cluster.
trigger.on-dx-deployment: |
- when: app.status.operationState.phase in ['Succeeded'] and app.status.health.status == 'Healthy' and app.spec.destination.server contains 'cluster/prod'
oncePer: app.status.operationState.startedAt
send: [dx-deployment]
subscriptions: |
- recipients:
- dx
triggers:
- on-dx-deployment
Four things to check when adapting this.
Keep reference_id stable across retries. Argo CD retries a delivery that does not return a 2xx, roughly every few minutes until the Application’s next sync, so the payload has to be idempotent. operationState.startedAt is stable across those retries but new for every sync operation, so retries collapse onto one deployment while a genuine redeploy of the same SHA still counts. Use that same field for the trigger’s oncePer: Argo CD’s dedupe and DX’s have to key on the same value, or one of them lets a duplicate through.
Confirm what sync.revision holds. For a git-backed Application it is the commit SHA, which is what commit_sha expects. For an Application that tracks a Helm chart or OCI artifact it is a chart version, and sending that as commit_sha silently breaks lead time attribution — the deployment still lands, but DX cannot match it to a pull request. In that case pull the SHA from wherever your chart carries it, commonly the image tag in .app.spec.source.helm.values:
{{- $vals := .app.spec.source.helm.values -}}
{{- $sha := "" -}}
{{- if regexMatch `(?s).*tag: '([^']+)'.*` $vals -}}
{{- $sha = regexReplaceAll `(?s).*tag: '([^']+)'.*` $vals `$1` -}}
{{- end -}}
Guard with regexMatch before calling regexReplaceAll, which returns its input unchanged when the pattern does not match. Without the guard, a values block that does not contain a tag puts the entire multi-line blob into commit_sha and the body stops being valid JSON. When the SHA cannot be resolved, omit commit_sha and repository rather than sending a bad value: the deployment still counts toward deploy frequency, and only lead time attribution is lost.
Choose a service identifier that survives your Application layout. .app.metadata.name is convenient, but if you run one Application per cluster or namespace for the same release, it fragments a single service into several. Prefer a value that names the service itself, and use the same identifier across deployments, incidents, and catalog services.
Scope the trigger to production only. Every cluster the trigger matches reports the same release again and inflates deploy frequency, so keep this cluster set narrower than the one you use for Slack notifications.
Errors
This table lists the expected errors that this method could return. However, other errors can be returned in the case where the service is down or other unexpected factors affect processing. Callers should always check the value of the ok param in the response.
| Error | Description |
|---|---|
not_authed |
This error occurs if the API request does not include a valid API key for authentication. |
invalid_auth |
The provided API key is invalid. This can happen if the key is expired or does not exist. |
account_inactive |
The user account associated with the API key is deactivated or suspended. |
invalid_json |
The JSON body of the request could not be parsed. This usually indicates a syntax error. |
required_params_missing |
One or more required parameters were not provided in the request. |
reference_id_exists |
A deployment already exists with the supplied reference_id. |
repository_not_found |
The specified repository could not be found. This could be due to a typo or an incorrect repository name. |