Appearance
Uploads
Upload a file attachment that can then be referenced in a thread or comment.
Upload File
POST /api/v1/uploadUploads a file to Pulse storage. Returns an attachment record with a URL that can be passed as an attachmentId when creating threads or comments.
Authentication
This endpoint uses publishable key + user JWT (the same credentials as the WebSocket connection), not the secret key. It is intended to be called from the browser via the Client SDK. For server-side usage, pass the user's JWT and publishable key directly.
X-Pulse-Key: pk_your_publishable_key
X-Pulse-Token: <user JWT>Request
Content-Type: multipart/form-data
| Field | Type | Required | Description |
|---|---|---|---|
file | File | Yes | The file to upload |
bash
curl -X POST https://pulse.hire.rest/api/v1/upload \
-H "X-Pulse-Key: pk_your_publishable_key" \
-H "X-Pulse-Token: eyJhbGci..." \
-F "file=@/path/to/image.png"Response 200
json
{
"id": "a1b2c3d4-...",
"type": "image",
"filename": "image.png",
"mimeType": "image/png",
"sizeBytes": 204800,
"url": "https://pulse.hire.rest/files/env_abc/a1b2c3d4-...",
"thumbnailUrl": "https://pulse.hire.rest/files/env_abc/a1b2c3d4-...?thumb=1",
"width": 1920,
"height": 1080
}| Field | Type | Description |
|---|---|---|
id | string | Attachment ID — pass this as an attachmentId |
type | "image" | "audio" | "video" | Detected media type |
filename | string | Original filename |
mimeType | string | MIME type of the file |
sizeBytes | number | File size in bytes |
url | string | Direct URL to serve the file |
thumbnailUrl | string? | Thumbnail URL (images only, 200×200 JPEG) |
durationMs | number? | Duration in milliseconds (audio/video) |
width | number? | Width in pixels (images only) |
height | number? | Height in pixels (images only) |
Supported MIME Types
| Type | MIME Types |
|---|---|
image | image/jpeg, image/png, image/gif, image/webp |
audio | audio/webm, audio/ogg, audio/mp4, audio/mpeg, audio/wav |
video | video/webm, video/mp4 |
Error Responses
| Status | Condition |
|---|---|
400 | No file provided, or unsupported MIME type |
401 | Missing or invalid X-Pulse-Key / X-Pulse-Token |
403 | The media type is disabled for this environment (see Environment Config) |
413 | File exceeds maxFileSizeMb for this environment (default 10 MB) |
json
{ "error": "Image uploads are disabled for this environment" }
{ "error": "File too large (max 10MB)" }Serving Files
Files are served at:
GET /files/:envId/:fileIdThis endpoint requires the same authentication as the upload endpoint (X-Pulse-Key and X-Pulse-Token headers). The authenticated environment must match the :envId in the URL.
Query parameters:
thumb=1— Returns a thumbnail version (JPEG, 200x200)
Response headers include Cache-Control: public, max-age=31536000, immutable.
Error Responses
| Status | Condition |
|---|---|
403 | Authenticated environment does not match the requested :envId |
404 | File not found |
Using Attachment IDs
After uploading, pass the returned id when creating a thread or comment so the attachment is linked to that content:
typescript
// Upload first
const attachment = await client.uploadFile(file);
// Then create a thread with the attachment
client.createThread('Check out this screenshot', {
attachmentIds: [attachment.id],
});
// Or reply with an attachment
client.reply('thread-id', 'Here is the updated version', [], [attachment.id]);Widget handles this automatically
When using <pulse-widget>, file selection, upload, and attachment linking all happen automatically. You only need this API if you're building a custom comment UI.