
# Upload a support artifact

`POST /v1/support-artifacts` · permission `support-artifacts:write`

Attaches a HAR file or a log to a support ticket. Artifacts are uploaded only from the dashboard Support page; API keys and machine clients receive `403 insufficient_permissions`. The dashboard removes secrets in your browser before the upload, and the server scans the artifact again: `Cookie`, `Set-Cookie`, `Authorization` and `DPoP` headers, OAuth, OIDC and SAML token fields, `__Host-` cookies, JWTs and KeenAuth-prefixed secrets are replaced with `[redacted]`. A management API key (`idp_sk_`, `idp_rk_`) found in the artifact is revoked at once; if revocation fails, nothing is stored and the upload returns `503`. Only the cleaned content is stored, encrypted with the environment's data key, and it is deleted after 30 days. The response never contains the content or the removed values.

## Request

| Field | Type | Required | Description |
|---|---|---|---|
| `ticket_ref` | query | yes | Support ticket reference: 1-64 characters, letters, digits and `_ . : -`, starting with a letter or digit. |
| `kind` | query | yes | `har` (a HAR 1.2 JSON document) or `log` (plain text). |
| `Content-Type` | header | yes | `application/octet-stream`. |
| `Content-Encoding` | header | no | `gzip` to send a compressed artifact. |
| `Idempotency-Key` | header | no | Optional. 16-64 characters `[A-Za-z0-9_-]`; repeating the same key with the same body returns the same artifact. |

The body is the artifact itself: at most 50 MB uncompressed. The request body on the wire is limited to 11 MiB, so compress larger artifacts with gzip. A ticket holds at most 10 artifacts.

## Response

`201 Created` with the stored artifact. `findings` counts the removed values by kind; `revocations_triggered` is the number of credentials revoked because they appeared in the artifact.

```json
{
  "id": "sart_01j9xk3m7q8r2s4t6v8w0y2z4a",
  "ticket_ref": "SUP-48213",
  "kind": "har",
  "size_bytes": 482113,
  "sha256": "9f2b5c7d1e3a4f60718293a4b5c6d7e8f90123456789abcdef0123456789abcd",
  "findings": {"authorization": 2, "cookie": 5, "host_cookie": 3, "oauth_param": 1},
  "revocations_triggered": 0,
  "created_at": "2026-11-10T09:30:00.123456Z",
  "expires_at": "2026-12-10T09:30:00.123456Z"
}
```

## Errors

| HTTP | code | When |
|---|---|---|
| 400 | `malformed_request` | The body could not be read, the gzip stream is invalid, or `Idempotency-Key` is malformed. |
| 401 | `unauthenticated` | The credential is missing, invalid or revoked. |
| 403 | `insufficient_permissions` | The request was not made from the dashboard, or the dashboard role lacks `support-artifacts:write`. |
| 403 | `quota_exceeded` | The ticket already has 10 artifacts. |
| 413 | `payload_too_large` | The artifact is larger than 50 MB, or the request body is larger than 11 MiB. |
| 415 | `unsupported_media_type` | `Content-Type` is not `application/octet-stream`, or the content coding is not `gzip`. |
| 422 | `validation_failed` | `ticket_ref` or `kind` is missing or invalid, the body is empty, or a `har` artifact is not a HAR document (`/body` `invalid_har`). |
| 422 | `idempotency_key_reuse` | The `Idempotency-Key` was already used with a different body. |
| 429 | `rate_limited` | Too many uploads are being processed or the rate limit was exceeded; retry after the `Retry-After` period. |
| 503 | `service_unavailable` | The artifact was not stored (a credential could not be revoked or storage is unavailable); retry after the `Retry-After` period. |

## Example

Uploads are a dashboard action. The request the dashboard sends:

```http
POST /v1/support-artifacts?ticket_ref=SUP-48213&kind=har
Content-Type: application/octet-stream
Content-Encoding: gzip
Idempotency-Key: 2f1c9a7e-5b3d-4e8f-9a6c-1d2e3f4a5b6c

<gzip-compressed HAR file>
```
