---
title: "Register a photo of the building"
method: POST
path: "/projects/{project}/angles"
tags: ["Views"]
---

# Register a photo of the building

`POST /projects/{project}/angles`

## Request

- Base URL: `https://api.getfacade.ai/api/v1`
- URL: `https://api.getfacade.ai/api/v1/projects/{project}/angles`
- Auth: one of:
  - HTTP bearer
  - OAuth 2 (authorization code, authorization URL `https://api.getfacade.ai/oauth/authorize`, token URL `https://api.getfacade.ai/oauth/token`, scopes: `agent`)

First of three legs. Returns an `upload_policy` with a presigned URL; PUT the bytes to it
(no `Authorization` header there, the signature is the authorisation), then confirm.
`aspect_ratio` is optional: given `width` and `height`, the server picks the nearest
supported ratio itself.

`POST /projects/{project}/uploads` is the endpoint to prefer: it accepts any file for
the building and its answer names what the file turned out to be.

## Path parameters

- `project` string, required

## Request body

- object
  - `data` object, required
    - `type` 'angle', required
    - `attributes` object, required
      - `md5` string, required
      - `width` integer, required
      - `height` integer, required
      - `aspect_ratio` string: Optional; derived from width/height when omitted
      - `file_name` string
      - `content_type` string

## Response `201`

Registered

- object
  - `data` object: JSON:API resource. `id` is a top-level field, never inside `attributes`.
    - `type` string, required
    - `id` string, required
    - `attributes` object
      - `upload_policy` UploadPolicy, nullable: Where to PUT the bytes. `null` once the upload is confirmed — there is nothing left to send. When `skip_upload` is true the server already holds these bytes: `url` is empty and the PUT is skipped, so go straight to confirm.
        - `url` string: Presigned target; empty string when `skip_upload` is true
        - `method` string
        - `headers` object: Send these verbatim with the PUT. Empty object when none are needed.
        - `expires_in` integer: Seconds the presigned URL stays valid. Absent when `skip_upload` is true.
        - `skip_upload` boolean: Present only when true.
    - `relationships` object

## Other responses

- `422`: The request was understood and refused. The message is the API's own text; show it unchanged. `IDEMPOTENCY_KEY_REQUIRED` — a paid call from an agent key with no `Idempotency-Key` header; nothing was queued, send it again with one. `IDEMPOTENCY_KEY_REUSED` — that header value already names a different request; a new order needs a new value.
- `429`: Rate limited on this key's own bucket, separate from the human sessions of the same account. Refusal is deliberately cheap: back off, do not retry in a tight loop.

---

[API](https://skmtc.dev/getfacade/apis/getfacade-agent-api.md) · [All operations](https://skmtc.dev/getfacade/apis/getfacade-agent-api/llms.txt) · [OpenAPI document](https://skmtc.dev/getfacade/apis/getfacade-agent-api/revisions/2b52eda7362c?raw)
