Treeship
Hub API

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
}
FieldTypeRequiredDescription
artifact_idstringYesContent-addressed artifact ID
payload_typestringYesDSSE payload type MIME string
envelope_jsonstringYesThe complete DSSE envelope as a JSON string
digeststringYesSHA-256 digest of the PAE bytes, prefixed with sha256:
signed_atintegerYesUnix timestamp of when the artifact was signed
parent_idstring or nullNoParent 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 method
  • htu: "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)" } }
FieldTypeDescription
artifact_idstringEchoed artifact ID
hub_urlstringPublic verification URL
rekor_indexinteger or nullSigstore Rekor global log index. Kept for older clients; read rekor instead
rekorobjectstatus (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

StatusBodyCause
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.