POST /v1/artifacts
Push a signed artifact to Hub. DPoP authenticated.
Request
POST /v1/artifacts
Authorization: DPoP hub_9f8e7d6c
DPoP: eyJhbGciOiJFZERTQSIsInR5cCI6ImRwb3Arand0In0...
Content-Type: application/json{
"artifact_id": "art_f7e6d5c4b3a2f7e6",
"payload_type": "application/vnd.treeship.action.v1+json",
"envelope_json": "{\"payload\":\"...\",\"payloadType\":\"...\",\"signatures\":[...]}",
"digest": "sha256:abc123...",
"signed_at": 1711500000,
"parent_id": null
}| Field | Type | Required | Description |
|---|---|---|---|
artifact_id | string | Yes | Content-addressed artifact ID |
payload_type | string | Yes | DSSE payload type MIME string |
envelope_json | string | Yes | The complete DSSE envelope as a JSON string |
digest | string | Yes | SHA-256 digest of the PAE bytes, prefixed with sha256: |
signed_at | integer | Yes | Unix timestamp of when the artifact was signed |
parent_id | string or null | No | Parent artifact ID for chain linking |
Authentication
This endpoint requires DPoP authentication. The Authorization header must be DPoP {hub_id}, and the DPoP header must be a valid JWT signed by the hub private key with:
htm: "POST"matching the HTTP methodhtu: "https://api.treeship.dev/v1/artifacts"matching the endpoint URL
See the Hub API overview for full DPoP details.
Response
{
"artifact_id": "art_f7e6d5c4b3a2f7e6",
"hub_url": "https://treeship.dev/verify/art_f7e6d5c4b3a2f7e6",
"rekor_index": 12345,
"rekor": {
"status": "anchored",
"log_index": 12345,
"entry": {
"uuid": "108e9186e8c5677a...",
"body": "eyJhcGlWZXJzaW9uIjoiMC4wLjEiLCJraW5kIjoiZHNzZSIs...",
"integratedTime": 1790201063,
"logID": "c0d23d6ad406973f9559f3ba2d1ca01f84147d8ffc5b8445c224f98b9591801d",
"logIndex": 12345,
"verification": {
"signedEntryTimestamp": "MEUCIBP8TCRCq86h...",
"inclusionProof": { "logIndex": 2804570999, "treeSize": 2804571155, "rootHash": "bb9c9abc...", "hashes": ["..."], "checkpoint": "rekor.sigstore.dev - ..." }
}
}
}
}A push whose anchoring did not succeed still returns 200, with the reason:
{ "rekor": { "status": "skipped", "reason": "no signature on this artifact was made by the dock's registered ship key (agent-key-only artifacts are not anchored per artifact)" } }| Field | Type | Description |
|---|---|---|
artifact_id | string | Echoed artifact ID |
hub_url | string | Public verification URL |
rekor_index | integer or null | Sigstore Rekor global log index. Kept for older clients; read rekor instead |
rekor | object | status (anchored, failed, skipped), reason when not anchored, log_index, and entry: the full Rekor log entry, which the CLI stores and treeship verify checks offline |
Errors
| Status | Body | Cause |
|---|---|---|
401 | {"error": "invalid DPoP proof"} | DPoP JWT invalid or expired |
401 | {"error": "hub not found"} | Unknown hub ID |
400 | {"error": "missing required field: artifact_id"} | Incomplete request body |
Rekor anchoring
The hub submits each pushed artifact to Sigstore Rekor as a dsse entry, keeping only signatures made by the dock's registered ship key. Rekor verifies those signatures and records the payload's SHA-256, the signatures and the key. It does not record the payload.
The hub does not ask you to trust it about the result. It returns Rekor's own entry, and treeship verify checks the signed entry timestamp, the inclusion proof, the signed checkpoint, and that the entry is for this artifact, all offline. A Rekor outage never fails the push: rekor.status is failed with the reason. Pushing the same artifact again retries anchoring when it has no entry yet.
Before TS-2026-003 every submission was rejected and the failure was hidden; no artifact pushed before the fix is anchored until it is pushed again.
Example
curl -X POST https://api.treeship.dev/v1/artifacts \
-H "Authorization: DPoP hub_9f8e7d6c" \
-H "DPoP: eyJhbGciOiJFZERTQSJ9..." \
-H "Content-Type: application/json" \
-d '{
"artifact_id": "art_f7e6d5c4b3a2f7e6",
"payload_type": "application/vnd.treeship.action.v1+json",
"envelope_json": "{...}",
"digest": "sha256:abc123...",
"signed_at": 1711500000,
"parent_id": null
}'In practice, you do not call this endpoint directly. The CLI handles artifact construction, signing, and pushing when you run treeship hub push.