Skip to content

Upload and Attach an Image

Assets are files attached to a resource — a room photo, a floor plan, a logo, a PDF, a video. The same flow works for any file type. You can upload in one request with a multipart form, or in two requests when you need to create the metadata row first and upload the bytes separately.

Single-Step Upload

POST /asset with multipart/form-data. Include the file in a field named file alongside the metadata fields.

bash
curl -X POST "https://api.bookable.samna.io/api/asset" \
  -u "<client_id>:<client_secret>" \
  -F "name=meeting-room-1.jpg" \
  -F "mime_type=image/jpeg" \
  -F "owner_id=a1b2c3d4-e5f6-7890-abcd-ef1234567890" \
  -F "object_id=c3d4e5f6-a7b8-9012-cdef-345678901234" \
  -F "object_type=public.bookable" \
  -F "file=@./meeting-room-1.jpg"

INFO

The curl command above is a reference example. Use whichever HTTP client fits your stack.

The asset row is created, the bytes are uploaded, and the row is returned with status: "complete" in one call.

Response:

json
{
  "data": {
    "id": "44444444-5555-6666-7777-888888888888",
    "name": "meeting-room-1.jpg",
    "description": null,
    "mime_type": "image/jpeg",
    "owner_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "created_by": "e992bfc1-0336-42c5-bd0a-4f4804a9fd24",
    "status": "complete",
    "index": 0,
    "asset_url": "https://assets.samna.io/44444444-5555-6666-7777-888888888888",
    "created_at": "2026-05-15T09:12:33.441Z",
    "updated_at": "2026-05-15T09:12:33.814Z"
  },
  "request_id": "b3c4d5e6-f7a8-9012-bcde-345678901234",
  "count": 1
}

Two-Step Upload

Use this when you need the asset id before the bytes are ready, or when the upload is handled separately from metadata creation.

1. Create the Asset Row

POST /asset

json
{
  "name": "meeting-room-1.jpg",
  "mime_type": "image/jpeg",
  "owner_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "object_id": "c3d4e5f6-a7b8-9012-cdef-345678901234",
  "object_type": "public.bookable"
}

object_id and object_type must be provided together or both omitted. If omitted, the asset is linked to the calling user. name and mime_type are required.

Response:

json
{
  "data": {
    "id": "44444444-5555-6666-7777-888888888888",
    "name": "meeting-room-1.jpg",
    "description": null,
    "mime_type": "image/jpeg",
    "owner_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "created_by": "e992bfc1-0336-42c5-bd0a-4f4804a9fd24",
    "status": "pending",
    "index": 0,
    "created_at": "2026-05-15T09:12:33.441Z",
    "updated_at": "2026-05-15T09:12:33.441Z"
  },
  "request_id": "a2b3c4d5-e6f7-8901-abcd-234567890123",
  "count": 1
}

The row is created with status: "pending". The asset cannot be served until the bytes arrive. Capture data.id as asset_id.

2. Upload the Bytes

POST /asset/{asset_id} with multipart/form-data and the file in a field named file.

bash
curl -X POST "https://api.bookable.samna.io/api/asset/44444444-5555-6666-7777-888888888888" \
  -u "<client_id>:<client_secret>" \
  -F "file=@./meeting-room-1.jpg"

INFO

The curl command above is a reference example. Use whichever HTTP client fits your stack.

The bytes are uploaded to blob storage, status moves to "complete", and the updated row is returned. Once complete the file is available at GET /asset/{asset_id}/data. Uploading to an asset that already has status: "complete" returns 404.

Reading the File

GET /asset/{asset_id}/data streams the bytes with the original Content-Type. Use this URL in <img src> directly.

GET /asset/{asset_id} returns the metadata row without the bytes.

Re-attaching

PUT /asset/{asset_id} with object_id and object_type moves the asset to a different resource. Set both to null to detach it.

Notes

  • The mime type set at creation is authoritative for Content-Type on the data endpoint. The server does not inspect the uploaded bytes.
  • Maximum upload size is determined by the deployment. Oversized files return 413 Payload Too Large.
  • To replace the bytes on a complete asset, delete the asset and upload a new one.