---
title: "Get an upload link (from an app)"
method: POST
path: "/app-files"
tags: ["Apps"]
---

# Get an upload link (from an app)

`POST /app-files`

## Request

- Base URL: `https://api.hypamoon.com/api/v1`
- URL: `https://api.hypamoon.com/api/v1/app-files`
- Auth: one of:
  - HTTP bearer
  - OAuth 2 (authorization code, authorization URL `https://app.hypamoon.com/oauth/authorize`, token URL `https://api.hypamoon.com/oauth/token`, scopes: `data:write`)

For an app's own server code only: fetch(env.HYPAMOON_API + "/app-files", { method: "POST", ... }) without an Authorization header; Hypamoon adds the app's key, and any other key gets 403. Returns a path on the site's own address where the browser PUTs the exact bytes, once, within 15 minutes. The type is checked against the bytes, PDFs and text go through the abuse check, and the size counts toward the files in the plan. Forward the x-hypamoon-user and x-hypamoon-user-sig headers of the request to make the file the signed-in user's own: it is stored in their own folder, only they (and users with the admin role, the first of auth.roles) can download it, and it goes with them on export or deletion. Only forward the headers; never check their signature in the code.

## Request body

- object
  - `name` string, required: The file name, like receipt.pdf.
  - `content_type` 'image/png' | 'image/jpeg' | 'image/gif' | 'image/webp' | 'application/pdf' | 'video/mp4' | 'video/webm' | 'audio/mpeg' | 'audio/mp4' | 'audio/webm' | 'image/heic' | 'image/heif' | 'video/quicktime' | 'text/plain' | 'text/csv' | 'application/json', required: The type: images (png, jpeg, gif, webp, heic, heif), pdf, video (mp4, webm, mov), audio (mp3, m4a, webm), plain text, csv or json. The bytes are checked against it.
  - `size` integer, required: The exact size in bytes (File.size in the browser).
  - `user_id` string, uuid: Leave it out. To make the file the signed-in person's own, forward the x-hypamoon-user and x-hypamoon-user-sig headers of the request; if you send user_id, it must be that same person.

## Response `201`

The upload link.

- object
  - `id` string, uuid, required
  - `upload_url` string, required: PUT the bytes here from the browser, on the site's own address. Works once.
  - `method` 'PUT', required
  - `expires_at` string, required

## Other responses

- `400`: Bad Request: invalid_json.
- `401`: Unauthorized: unauthorized, missing_api_key, invalid_api_key, api_key_revoked, project_deleted.
- `402`: Payment Required: plan_limit_reached.
- `403`: Forbidden: project_not_enabled, key_mode_mismatch, missing_scope, verification_required, forbidden, site_offline.
- `409`: Conflict: plan_limit_reached, conflict.
- `413`: Error: file_too_large.
- `422`: Unprocessable Content: validation_failed.
- `429`: Too Many Requests: rate_limited.
- `500`: Internal Server Error: internal_error.
- `503`: Service Unavailable: service_unavailable.

---

[API](https://skmtc.dev/hypamoon/apis/hypamoon-api.md) · [All operations](https://skmtc.dev/hypamoon/apis/hypamoon-api/llms.txt) · [OpenAPI document](https://skmtc.dev/hypamoon/apis/hypamoon-api/revisions/9f0b90004c06?raw)
