Documentation

File uploads

Multipart upload endpoints, S3-backed storage, signed URLs, and customer-scoped files.

AppEngine ships a generic file-storage layer used by every domain — site assets, product images, attachments, customer documents, generated favicons. Files are stored in S3 (or a configured equivalent) under per-org prefixes; the API hides the bucket details.

Endpoints

POST/repository/file/uploadJWT
POST/repository/file/upload-urlJWT
POST/repository/fileJWT
POST/repository/file/bufferJWT
POST/repository/file/streamJWT
POST/repository/file/urlJWT
POST/repository/file/signurlJWT
POST/repository/file/existsJWT
POST/repository/file/statJWT
POST/repository/file/deleteJWT
POST/repository/file/moveJWT
POST/repository/file/copyJWT
POST/repository/file/createfolderJWT
POST/repository/file/thumbnailsJWT
POST/repository/file/make-privateJWT
POST/repository/file/make-publicJWT
GET/repository/file/flatlist/{prefix}/{pageNumber}JWT
POST/repository/file/flatlistJWT

Customer-scoped variants (write under a per-customer prefix, callable by signed-in customers):

POST/repository/customer/file/uploadJWT
POST/repository/customer/file/flatlistJWT
POST/repository/customer/file/deleteJWT

Upload

The upload endpoint is multipart. The form fields are:

FieldTypeDescription
file*binary

The file part. Must be named file.

locationstring

Folder prefix the file is stored under. The final key is {location}/{originalFilename}.

metadatastring

Optional JSON-stringified metadata stored alongside the object.

isPrivatestring

"true" to make the object private (not directly readable from the public bucket URL). Defaults to public.

// from base-app/src/lib/appmint-client.ts (excerpt, simplified)
const formData = new FormData();
formData.append('location', 'products/images');
formData.append('files', file);

const res = await fetch('/api/repository/file/upload', {
  method: 'POST',
  headers: { Authorization: `Bearer ${token}`, orgid: orgId },
  body: formData,
});
const saved = await res.json();
// saved.url, saved.location, saved.metadata

Upload from URL

Avoid downloading-then-uploading by letting the server fetch on your behalf:

curl -X POST https://appengine.appmint.io/repository/file/upload-url \
  -H "orgid: my-org" -H "Authorization: Bearer $JWT" \
  -H "Content-Type: application/json" \
  -d '{"url":"https://supplier.example/image-1.jpg","location":"products/images"}'

Useful for CSV imports, vendor catalog sync, and AI-generated assets.

Read

POST /repository/file returns the file content. Pass encoding: 'utf8' for text files, omit for base64. For large files use /file/buffer (raw bytes) or /file/stream (returns a presigned stream URL).

curl -X POST https://appengine.appmint.io/repository/file \
  -H "orgid: my-org" -H "Authorization: Bearer $JWT" \
  -H "Content-Type: application/json" \
  -d '{"location":"products/images/mug.jpg"}'

Public URLs

Use /repository/file/url to fetch a stable public URL for a file. The shape is:

https://{cdn-host}/{orgId}/{location}

For private files, use /repository/file/signurl to get a time-limited presigned URL:

curl -X POST https://appengine.appmint.io/repository/file/signurl \
  -H "orgid: my-org" -H "Authorization: Bearer $JWT" \
  -H "Content-Type: application/json" \
  -d '{"location":"orders/2024/invoice-123.pdf","options":{"expiresIn":3600}}'

Privacy toggles

Files default to public read. Flip a single file or a batch with:

# make private
curl -X POST https://appengine.appmint.io/repository/file/make-private \
  -H "orgid: my-org" -H "Authorization: Bearer $JWT" \
  -H "Content-Type: application/json" \
  -d '{"locations":["customer-data/contracts/contract-1.pdf"]}'

# make public
curl -X POST https://appengine.appmint.io/repository/file/make-public \
  -H "orgid: my-org" -H "Authorization: Bearer $JWT" \
  -H "Content-Type: application/json" \
  -d '{"locations":["products/hero.jpg"]}'

Listing

flatlist returns a flat (non-recursive) listing for a prefix:

curl 'https://appengine.appmint.io/repository/file/flatlist/products%2Fimages/1' \
  -H "orgid: my-org" -H "Authorization: Bearer $JWT"

Or POST for richer filtering (date range, mime type, size):

curl -X POST https://appengine.appmint.io/repository/file/flatlist \
  -H "orgid: my-org" -H "Authorization: Bearer $JWT" \
  -H "Content-Type: application/json" \
  -d '{"prefix":"products/images","page":1,"pageSize":50}'

Thumbnails

POST /repository/file/thumbnails triggers thumbnail generation for an image. Sizes are configurable per org; the response contains URLs for each generated variant. Generation is synchronous for small images, queued otherwise.

For ad-hoc transforms (resize, crop, format), pass query params on the file URL — the CDN reads them and serves a transformed copy. Supported params are platform-specific; see the asset CDN docs.

Customer-scoped uploads

Files uploaded through /repository/customer/file/upload live under a per-customer prefix and are listable only by that customer (or a staff user). Use these for KYC documents, support attachments, profile avatars — anywhere the buyer "owns" the file.

curl -X POST https://appengine.appmint.io/repository/customer/file/upload \
  -H "orgid: my-org" -H "Authorization: Bearer $CUSTOMER_JWT" \
  -H "Content-Type: application/json" \
  -d '{"fileName":"receipt-2024.pdf","base64Data":"JVBERi0xLj..."}'

The customer endpoint takes base64 in JSON rather than multipart — handy when you can't post binary form-data (some mobile WebViews, some serverless platforms).

Quotas and limits

  • Per-file size cap defaults to 50 MB; configurable per org plan.
  • Filenames are sanitized — slashes, control characters, and non-ASCII whitespace are stripped.
  • Duplicate names overwrite by default. Pass a unique location (e.g. prepend a UUID) to keep both.
  • Deletes are immediate and not soft-deleted. There is no trash for files.

Error envelope

Same as the rest of AppEngine: { statusCode, message, error }. Common cases:

  • 400 — missing file part, or location contains invalid characters.
  • 413 — file exceeds the size cap.
  • 403 — JWT lacks update permission for the storage scope.
  • 404read against a missing key (also returned for read-on-private without a signed URL).