Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/PloutusLab/krafta-web/llms.txt

Use this file to discover all available pages before exploring further.

The Upload API accepts design files from customers and creators before they are attached to an order. Every file is validated by inspecting its raw magic bytes — not just its MIME type or extension — to prevent spoofing. Files are stored in Supabase Storage when configured, or written to the local filesystem in development. A DesignUpload record is created in the database either way, and its UUID is what you pass to the order creation step.

POST /api/upload

Upload a single design file. The request must be multipart/form-data. Authentication is optional — uploads from anonymous users are scoped to the "anonymous" storage path unless a userId is provided.

Request

Content-Type: multipart/form-data
file
File
required
The design file to upload. Accepted formats: PNG, JPEG, PDF, and SVG. Maximum size is 20 MB (20 × 1,048,576 bytes).
userId
string
User identifier used to namespace the storage path ({userId}/{uuid}.ext). Defaults to "anonymous" if omitted.

Accepted file types

The server reads the first 8 bytes of every upload and compares them against known magic byte signatures. The declared Content-Type header is ignored for validation purposes.
FormatMagic bytes (hex)ExtensionNotes
PNG89504E470D0A1A0A.pngFull 8-byte signature matched.
JPEGFFD8FF.jpgFirst 3 bytes matched; remainder of header may vary by encoder.
PDF25504446 (%PDF).pdfStored in the receipts bucket in Supabase; local path uses uploads/{userId}/.
SVG<svg or <?xml.svgValidated as UTF-8 text. Any SVG containing a <script> tag is rejected with 400.

File size limit

Files larger than 20 MB (20 × 1,024 × 1,024 bytes) are rejected before any storage or database write occurs.

Storage behavior

The route checks at startup whether NEXT_PUBLIC_SUPABASE_URL and NEXT_PUBLIC_SUPABASE_ANON_KEY are present and do not contain the placeholder strings. The storage path follows the pattern {userId}/{uuid}{ext} in both cases.
EnvironmentStorage targetURL format
Supabase configureddesigns bucket (images) or receipts bucket (PDFs)Internal Supabase path — signed URL generated on demand
No Supabase (development)public/uploads/{userId}/{uuid}{ext} on the local filesystem/uploads/{userId}/{uuid}{ext}
The file name written to storage is always a freshly generated UUID to prevent path traversal attacks. The original file.name is preserved only in the DesignUpload.fileName database field.

Response

{
  "success": true,
  "designId": "c91a2b3d-44e5-6f70-8901-23456789abcd",
  "url": "/uploads/user-123/c91a2b3d-44e5-6f70-8901-23456789abcd.png",
  "fileName": "my-logo.png",
  "mimeType": "image/png"
}
success
boolean
Always true on a successful upload.
designId
string
UUID of the DesignUpload record created in the database. Pass this value as designUploadId when creating an OrderItem to link the uploaded file to the order.
url
string
Public-accessible URL (local filesystem) or internal Supabase storage path. For Supabase-backed uploads, generate a signed URL server-side before displaying it to users.
fileName
string
Original file name as submitted by the client.
mimeType
string
MIME type resolved from magic byte inspection, not from the client header. Possible values: image/png, image/jpeg, application/pdf, image/svg+xml.

Error responses

StatusCondition
400No file field present in the form data.
400File size exceeds 20 MB.
400SVG file contains a <script> element.
400File type not recognized (magic bytes do not match PNG, JPEG, PDF, or SVG).
500Storage write or database error.

DesignUpload model reference

The following fields are written to the DesignUpload table on every successful upload.
ColumnTypeDescription
idString (UUID)Primary key — returned as designId in the response.
fileUrlStringStorage path or public URL.
fileNameStringOriginal client-supplied file name.
fileSizeIntFile size in bytes.
mimeTypeStringResolved MIME type from magic byte validation.
magicBytesString?First 16 hex characters of the file, stored for audit purposes.
userIdStringThe userId form field value, or "anonymous".
createdAtDateTimeServer timestamp at time of upload.
The magicBytes column stores only the first 16 hex characters (8 bytes) of the binary content. This is sufficient to verify the file type during any future audit without re-reading the file from storage.

Build docs developers (and LLMs) love