Flow
ServicesDocuments Service

Getting started

Run the documents service against the local Azurite emulator and walk the two-step upload with curl.

Prerequisites

Follow Local development. The documents service needs the Azure Blob Storage emulator as well as Postgres, and ./localenv up starts both:

./localenv up documents-api

Local catalog entries all point at the one Azurite instance and differ by container name. A catalog entry may own several containers, in which case a request names the one it wants. The service creates every configured container on startup and sets emulator CORS so a browser upload from the review console works; curl does not need that. Credentials come from services/documents/process-compose.yaml; the deployed equivalents are described in Operations.

Authorization is not wired yet — endpoints are open aside from Idempotency-Key on writes.

Upload a document

Ask for a write SAS

curl -s -X POST localhost:8102/v1/documents/sas \
  -H 'Content-Type: application/json' \
  -d '{"filename":"lease.pdf","visibility":"private","storage_account":"default-private"}' | jq

The response carries sas_url, sas_expires_at, the container it signed for, and the blob_name the service chose. Keep the URL, the container, and the blob name.

default-private owns one container, so the request can leave container out. An account configured with several requires it — add "container":"project-storage" to the body.

PUT the bytes to the SAS URL

curl -s -X PUT "$SAS_URL" \
  -H 'x-ms-blob-type: BlockBlob' \
  -H 'Content-Type: application/pdf' \
  --data-binary @lease.pdf

x-ms-blob-type is required by Azure for a block blob upload. Nothing reaches the documents service here.

Record the document

curl -s -X POST localhost:8102/v1/documents \
  -H "Idempotency-Key: $(uuidgen)" \
  -H 'Content-Type: application/json' \
  -d '{
        "data": {
          "filename": "lease.pdf",
          "visibility": "private",
          "storage_account": "default-private",
          "blob_name": "'"$BLOB_NAME"'",
          "content_type": "application/pdf",
          "references": [
            {"ref_type":"lease","ref_id":"L-1","ref_category":"signed-copy","is_external":false}
          ],
          "metadata": [{"meta_name":"pages","meta_value":"12"}]
        }
      }' | jq

The service confirms the blob exists before it writes the row, so a create that skipped step two returns 422 and stores nothing.

Read it back

curl -s localhost:8102/v1/documents/{id} | jq

The response is the document plus a freshly signed read sas_url. Fetch the bytes with that URL directly; the service never proxies them.

Find documents by reference

curl -s 'localhost:8102/v1/documents?ref_type=lease&ref_id=L-1' | jq

Each returned document carries all of its references and metadata, not only the one that matched. See API for every filter and ADR 0014 for the pagination shape.

Watch the fanout

In another terminal:

./localenv up documents-documentslistener

Creating or updating a document logs an event received line from documents-documents-queue-listener, carrying the same requestId the API returned in X-Request-Id. See ADR 0017.

Generate OpenAPI

go run ./services/documents/cmd/api -openapi > /tmp/documents-openapi.yaml

The checked-in spec is services/documents/openapi.yaml.

On this page