# Patch File

Partially updates an existing file identified by its filename (`reference`), which is unique per tenant — substitute its contents via a new `url` and/or replace its `tags`. At least one of these fields must be supplied.
The replacement `url` can point at a file with any name — the contents are **always stored under the
file's current `reference`**, because that filename is how the rest of the platform points at the file
and a substitution cannot rename it. What the replacement *cannot* change is the **file type**, or the
file would end up served under a name that misdescribes it.
That type is decided by **the content of the file**, not by the extension in the `url`: the extension
is yours to write and any file can be published under any name, so it proves nothing. The extension
still has to be one `POST /files` accepts, but the bytes are what settle the type. `POST /files`
reads the bytes the same way; there the type they are held to is the one the new file name announces,
here it is the type the file already has.

```
PATCH /files(product-datasheet.pdf)
{ "url": "https://cdn.example.com/docs/v2/product-datasheet.pdf" }   OK   same name, PDF content
{ "url": "https://cdn.example.com/exports/9f21b0-final.pdf" }        OK   any name, PDF content
{ "url": "https://cdn.example.com/docs/product-datasheet.docx" }     400  content is not a PDF
{ "url": "https://cdn.example.com/docs/product-datasheet.pdf" }      400  named .pdf, but not a PDF
```
### Changing a file's type
Not possible through a substitution, because the filename — and so the extension — is fixed. Do it
client-side in three steps:
1. **Create the new file** — `POST /files` with the new file's `url`.
2. **Repoint the references** — use the Catalog API to find every entity (product, variant, category, …)
that uses the old filename and update it to the new one.
3. **Delete the old file** — `DELETE /files({old-reference})` once nothing references it any more.

## Fields
- **url** (optional): public URL of the new file. Triggers a full substitution. May be called
anything, but must serve a file of the current file type — see above. The address itself is
validated exactly as on `POST /files`, so only the extensions that endpoint accepts are allowed
and the served `Content-Type` is not inspected.
- **tags** (optional): array of strings, validated exactly as on `POST /files`.
  - Absent → tags are not modified, **including when `url` is supplied**.
  - `[]` → all tags are cleared.
  - `["a","b"]` → tags are replaced with these values.

## Valid combinations
| Body | Operation |
|  --- | --- |
| `{ "url": "https://.../{any-filename}" }` | Substitute file contents — **tags are kept** |
| `{ "url": "https://.../{any-filename}", "tags": ["a"] }` | Substitute contents and set tags |
| `{ "tags": ["a"] }` | Update tags only, file contents unchanged |

## Invalid combinations
- Empty body `{}` → 400.
- `url` serving content that is not of the current file type → 400 (see above).

## Response (204 No Content)
Success returns no body.
## Notes
- **The replacement is live as soon as this returns.**
- A substitution does not change the file's `reference` or `fileType`, so entities referencing it
keep working.
- Unlike the image endpoint this operation has no 409 conflict: only images are queued for
background processing, so a file is never locked by an in-flight process.
- Tags behave as standard PATCH fields — absent means unchanged, exactly as on
`PATCH /images({reference})`.

## Error Responses
- **400 Bad Request**: Invalid request body (e.g., both fields missing), a replacement whose content
is not of the current `fileType`, or any of the `url`/`tags` validation failures documented on
`POST /files` (format, accessibility, extension, size). Same
`{ "validationFailures": { "error": [...] } }` envelope
- **401 Unauthorized**: Missing or invalid API key. Returned by the API gateway as
`{ "message": "Unauthorized", "request_id": "d8aafa5b8f3e400b60bea0123dd33317" }`
- **403 Forbidden**: The API key does not have write permissions for this operation. Same body
shape as the 401
- **404 Not Found**: No file with the given reference exists for this tenant. The body is the
message string, e.g. `"File with reference 'missing.pdf' not found"`
- **500 Internal Server Error**: Technical error (network failure fetching the replacement file,
storage or database unavailable). The already-uploaded replacement object is cleaned up, so the
file is left on its previous contents

Endpoint: PATCH /files({reference})
Version: 2.0.0

## Path parameters:

  - `reference` (string, required)
    File name (reference), unique per tenant (e.g. `product-datasheet.pdf`)

## Header parameters:

  - `X-API-KEY` (string, required)
    Tenant's API key (required)

## Request fields (application/json):

  - `reference` (string)
    Taken from the URL path, not from the request body — any value sent in the body is ignored.

  - `url` (string)
    Publicly accessible URL of the replacement file, which triggers a full substitution. Optional,
but at least one of `url` or `tags` must be supplied. Validated exactly as on create.
The replacement may be called anything at its own address — it is stored under the file's current
Sl.Apps.Dam.Application.Files.PatchFile.PatchFileRequest.Reference — but it must be a file of the same file type.

  - `tags` (array)
    Tags to set on the file. Optional. Omit to leave the tags untouched, send `[]` to clear them,
or send values to replace them.

## Response 401 fields (application/json):

  - `message` (string)
    Error description, e.g. `Unauthorized`.

  - `request_id` (string)
    Gateway request identifier, to quote when contacting support.

## Response 403 fields (application/json):

  - `message` (string)

  - `request_id` (string)

## Response 500 fields (application/json):

  - `error` (string)

  - `details` (any)

