ReferenceReference by module

Files API

Every Files action (14) and data model (3): endpoints, scopes, input, preconditions, events and examples.

Every file you upload, create or receive, in folders with previews.

Module code files · 14 actions · 3 data models · 16 events. A key or token never does more here than the person behind it can; row-level security applies exactly as in the app. Scopes: files (everything), files:read (read only) or files:<capability>.

Resources

ResourceTableList and readPOSTPATCHDELETE
file_linksfile_link/api/v1/file_links
file_upload_linksfile_upload_link/api/v1/file_upload_links
filesfile/api/v1/filesfiles.update

Lists take limit (up to 200), cursor, order (e.g. -created_at), fields and q; every other query parameter is a column filter: ?status=paid, ?total[gte]=100, ?id[in]=a,b, ?x=null.

Actions

ActionNeedsWhat it does
files.attachfiles:readAttach a file to a record
files.create_folderfiles:writeCreate a folder
files.create_linkfiles:manageCreate an upload link
files.detachfiles:readRemove a file from a record
files.publishfiles:managePublish a file
files.removefiles:deleteDelete a file or folder
files.renew_linkfiles:manageExtend an upload link
files.replacefiles:writeSave new contents
files.revoke_linkfiles:manageClose an upload link
files.sign_urlfiles:readGet a download link
files.sweepfiles:manageClean up files
files.unpublishfiles:manageMake a file private
files.updatefiles:writeEdit file details
files.uploadfiles:writeRecord an uploaded file

files.attach

Attach a file to a record — Puts a file from Files on a record’s Files tab. Needs permission to edit that record.

Needs files:read · changes data

POSThttps://app.getera.io/api/v1/file_links/actions/attach
API key or OAuth token · files:read
  • Canonical address: POST /api/v1/actions/files.attach

Input

FieldTypeRequiredNotes
fileIduuidyesFile · id of file
recordTypestringyesRecord type · max 60
recordIduuidyesRecord

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
unknown_record_typeThe record type must be one a module owns.
cannot_write_recordAttaching needs write on that record’s module.
file_not_foundThe file must exist and not be a folder.
record_not_foundThe record must exist.

Events it publishes: files.attached

What it writes: file_link (insert) — Links the file to the record

curl -X POST https://app.getera.io/api/v1/file_links/actions/attach \
  -H "Authorization: Bearer $ERA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"fileId":"FILE_ID","recordType":"Record type","recordId":"RECORD_ID"}'
Response
{
  "ok": true,
  "result": "…",
  "events": [
    "files.attached"
  ]
}
import { createEra } from "@getera/sdk"

const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.fileLinks.attach({
  fileId: "FILE_ID",
  recordType: "Record type",
  recordId: "RECORD_ID"
})
era file_links attach \
  --file-id "FILE_ID" \
  --record-type "Record type" \
  --record-id "RECORD_ID"
tools/call
{
  "name": "era__files__attach",
  "arguments": {
    "fileId": "FILE_ID",
    "recordType": "Record type",
    "recordId": "RECORD_ID"
  }
}

Errors: 401 no or expired credential · 403 the credential does not hold files:read · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 unknown_record_type, cannot_write_record, file_not_found, record_not_found · 429 slow down

files.create_folder

Create a folder — Adds a folder under an existing path, refusing a name that is already taken there.

Needs files:write · changes data

POSThttps://app.getera.io/api/v1/files/actions/create_folder
API key or OAuth token · files:write
  • Canonical address: POST /api/v1/actions/files.create_folder

Input

FieldTypeRequiredNotes
parentPathstringInside · max 500
namestringyesName · max 255

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
name_requiredA folder needs a name.
name_has_separatorA folder name cannot contain a slash.
folder_existsNo sibling folder may already carry that name.

Events it publishes: files.folder_created

What it writes: file (insert) — Adds the folder row

curl -X POST https://app.getera.io/api/v1/files/actions/create_folder \
  -H "Authorization: Bearer $ERA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"name":"Name"}'
Response
{
  "ok": true,
  "result": "…",
  "events": [
    "files.folder_created"
  ]
}
import { createEra } from "@getera/sdk"

const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.files.createFolder({
  name: "Name"
})
era files create_folder \
  --name "Name"
tools/call
{
  "name": "era__files__create_folder",
  "arguments": {
    "name": "Name"
  }
}

Errors: 401 no or expired credential · 403 the credential does not hold files:write · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 name_required, name_has_separator, folder_exists · 429 slow down

Create an upload link — Opens a tokenised door for someone outside the workspace to upload into one folder.

Needs files:manage · changes data

POSThttps://app.getera.io/api/v1/file_upload_links/actions/create_link
API key or OAuth token · files:manage
  • Canonical address: POST /api/v1/actions/files.create_link
FieldTypeRequiredNotes
labelstringLabel · max 255
descriptionstringDescription · max 2000
targetFolderstringUploads land in · max 500
maxFilesnumberMaximum files
maxFileSizeMbnumberMaximum size (MB)
allowedFileTypesstringAccepted types · Comma-separated extensions or mime prefixes. Empty accepts everything. · max 500
expiresAtdate-timeExpires

The action is refused with 409 and one of these codes:

CodeRefused when
invalid_limitsThe link must accept at least one file of at least one megabyte.
expiry_in_pastThe expiry date must be in the future.

Events it publishes: files.link_created

What it writes: file_upload_link (insert) — Creates the link and its token

curl -X POST https://app.getera.io/api/v1/file_upload_links/actions/create_link \
  -H "Authorization: Bearer $ERA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"label":"Label"}'
Response
{
  "ok": true,
  "result": "…",
  "events": [
    "files.link_created"
  ]
}
import { createEra } from "@getera/sdk"

const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.fileUploadLinks.createLink({
  label: "Label"
})
era file_upload_links create_link \
  --label "Label"
tools/call
{
  "name": "era__files__create_link",
  "arguments": {
    "label": "Label"
  }
}

Errors: 401 no or expired credential · 403 the credential does not hold files:manage · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 invalid_limits, expiry_in_past · 429 slow down

files.detach

Remove a file from a record — Takes a file off a record without deleting the file.

Needs files:read · removes or cannot be undone

POSThttps://app.getera.io/api/v1/file_links/actions/detach
API key or OAuth token · files:read
  • Canonical address: POST /api/v1/actions/files.detach

Input

FieldTypeRequiredNotes
fileIduuidyesFile · id of file
recordTypestringyesRecord type · max 60
recordIduuidyesRecord

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
unknown_record_typeThe record type must be one a module owns.
cannot_write_recordRemoving needs write on that record’s module.

Events it publishes: files.detached

What it writes: file_link (delete) — Unlinks the file from the record

curl -X POST https://app.getera.io/api/v1/file_links/actions/detach \
  -H "Authorization: Bearer $ERA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"fileId":"FILE_ID","recordType":"Record type","recordId":"RECORD_ID"}'
Response
{
  "ok": true,
  "result": "…",
  "events": [
    "files.detached"
  ]
}
import { createEra } from "@getera/sdk"

const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.fileLinks.detach({
  fileId: "FILE_ID",
  recordType: "Record type",
  recordId: "RECORD_ID"
})
era file_links detach \
  --file-id "FILE_ID" \
  --record-type "Record type" \
  --record-id "RECORD_ID"
tools/call
{
  "name": "era__files__detach",
  "arguments": {
    "fileId": "FILE_ID",
    "recordType": "Record type",
    "recordId": "RECORD_ID"
  }
}

Errors: 401 no or expired credential · 403 the credential does not hold files:read · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 unknown_record_type, cannot_write_record · 429 slow down

files.publish

Publish a file — Makes a file a public asset, served to anybody at its public address.

Needs files:manage · reaches outside (sends, publishes or charges) · asks a person to confirm when Era runs it

POSThttps://app.getera.io/api/v1/files/{id}/actions/publish
API key or OAuth token · files:manage
  • Canonical address: POST /api/v1/actions/files.publish

Input

FieldTypeRequiredNotes
iduuidyesFilled from the path {id} · File · id of file

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
not_foundThe file must still exist in this workspace.
is_folderA folder cannot be published.
already_publicThe file must be private.
uploaded_through_linkA file sent in by a stranger is re-uploaded before it is published.
no_objectThe entry must have a stored file behind it.

Events it publishes: files.published

What it writes: file (update) — Sets is_public

curl -X POST https://app.getera.io/api/v1/files/FILE_ID/actions/publish \
  -H "Authorization: Bearer $ERA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{}'
Response
{
  "ok": true,
  "result": "…",
  "events": [
    "files.published"
  ]
}
import { createEra } from "@getera/sdk"

const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.files.publish("FILE_ID")
era files publish FILE_ID
tools/call
{
  "name": "era__files__publish",
  "arguments": {
    "id": "FILE_ID"
  }
}

Errors: 401 no or expired credential · 403 the credential does not hold files:manage · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 not_found, is_folder, already_public, uploaded_through_link, no_object · 429 slow down

files.remove

Delete a file or folder — Tombstones the entry — and, for a folder, everything beneath it — and returns the objects to purge.

Needs files:delete · removes or cannot be undone · asks a person to confirm when Era runs it

POSThttps://app.getera.io/api/v1/files/{id}/actions/remove
API key or OAuth token · files:delete
  • Canonical address: POST /api/v1/actions/files.remove

Input

FieldTypeRequiredNotes
iduuidyesFilled from the path {id} · File · id of file

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
not_foundThe file must still exist in this workspace.

Events it publishes: files.deleted, files.folder_deleted

What it writes: file (update) — Marks the row, and its descendants, deleted

curl -X POST https://app.getera.io/api/v1/files/FILE_ID/actions/remove \
  -H "Authorization: Bearer $ERA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{}'
Response
{
  "ok": true,
  "result": "…",
  "events": [
    "files.deleted",
    "files.folder_deleted"
  ]
}
import { createEra } from "@getera/sdk"

const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.files.remove("FILE_ID")
era files remove FILE_ID
tools/call
{
  "name": "era__files__remove",
  "arguments": {
    "id": "FILE_ID"
  }
}

Errors: 401 no or expired credential · 403 the credential does not hold files:delete · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 not_found · 429 slow down

Extend an upload link — Moves an open link’s expiry date without changing the address already sent.

Needs files:manage · changes data

POSThttps://app.getera.io/api/v1/file_upload_links/{id}/actions/renew_link
API key or OAuth token · files:manage
  • Canonical address: POST /api/v1/actions/files.renew_link
FieldTypeRequiredNotes
iduuidyesFilled from the path {id} · Link · id of file_upload_link
expiresAtdate-timeyesOpen until

The action is refused with 409 and one of these codes:

CodeRefused when
not_foundThe link must exist in this workspace.
already_revokedA closed link is not reopened; a new one is made instead.
expiry_in_pastThe new expiry date must be in the future.

Events it publishes: files.link_renewed

What it writes: file_upload_link (update) — Moves the expiry date

curl -X POST https://app.getera.io/api/v1/file_upload_links/FILE_UPLOAD_LINK_ID/actions/renew_link \
  -H "Authorization: Bearer $ERA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"expiresAt":"2026-11-02T09:00:00Z"}'
Response
{
  "ok": true,
  "result": "…",
  "events": [
    "files.link_renewed"
  ]
}
import { createEra } from "@getera/sdk"

const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.fileUploadLinks.renewLink("FILE_UPLOAD_LINK_ID", {
  expiresAt: "2026-11-02T09:00:00Z"
})
era file_upload_links renew_link FILE_UPLOAD_LINK_ID \
  --expires-at "2026-11-02T09:00:00Z"
tools/call
{
  "name": "era__files__renew_link",
  "arguments": {
    "id": "FILE_UPLOAD_LINK_ID",
    "expiresAt": "2026-11-02T09:00:00Z"
  }
}

Errors: 401 no or expired credential · 403 the credential does not hold files:manage · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 not_found, already_revoked, expiry_in_past · 429 slow down

files.replace

Save new contents — Authorises overwriting a file’s bytes and records the new size as a new version.

Needs files:write · changes data

POSThttps://app.getera.io/api/v1/files/{id}/actions/replace
API key or OAuth token · files:write
  • Canonical address: POST /api/v1/actions/files.replace

Input

FieldTypeRequiredNotes
iduuidyesFilled from the path {id} · File · id of file
fileSizenumberyesNew size in bytes · min 0

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
not_foundThe file must still exist in this workspace.
is_folderA folder has no contents to save.
no_objectThe entry must have a stored file behind it.
file_too_largeThe new contents must be within the size limit.
storage_quota_exceededThe organization must have room left on its plan for the growth.

Events it publishes: files.replaced

What it writes: file (update) — Records the new size and version

curl -X POST https://app.getera.io/api/v1/files/FILE_ID/actions/replace \
  -H "Authorization: Bearer $ERA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"fileSize":1}'
Response
{
  "ok": true,
  "result": "…",
  "events": [
    "files.replaced"
  ]
}
import { createEra } from "@getera/sdk"

const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.files.replace("FILE_ID", {
  fileSize: 1
})
era files replace FILE_ID \
  --file-size 1
tools/call
{
  "name": "era__files__replace",
  "arguments": {
    "id": "FILE_ID",
    "fileSize": 1
  }
}

Errors: 401 no or expired credential · 403 the credential does not hold files:write · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 not_found, is_folder, no_object, file_too_large, storage_quota_exceeded · 429 slow down

Close an upload link — Stops a token being accepted, without touching the files that already came through it.

Needs files:manage · removes or cannot be undone · asks a person to confirm when Era runs it

POSThttps://app.getera.io/api/v1/file_upload_links/{id}/actions/revoke_link
API key or OAuth token · files:manage
  • Canonical address: POST /api/v1/actions/files.revoke_link
FieldTypeRequiredNotes
iduuidyesFilled from the path {id} · Link · id of file_upload_link

The action is refused with 409 and one of these codes:

CodeRefused when
not_foundThe link must exist in this workspace.
already_revokedThe link must still be open.

Events it publishes: files.link_revoked

What it writes: file_upload_link (update) — Closes the link

curl -X POST https://app.getera.io/api/v1/file_upload_links/FILE_UPLOAD_LINK_ID/actions/revoke_link \
  -H "Authorization: Bearer $ERA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{}'
Response
{
  "ok": true,
  "result": "…",
  "events": [
    "files.link_revoked"
  ]
}
import { createEra } from "@getera/sdk"

const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.fileUploadLinks.revokeLink("FILE_UPLOAD_LINK_ID")
era file_upload_links revoke_link FILE_UPLOAD_LINK_ID
tools/call
{
  "name": "era__files__revoke_link",
  "arguments": {
    "id": "FILE_UPLOAD_LINK_ID"
  }
}

Errors: 401 no or expired credential · 403 the credential does not hold files:manage · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 not_found, already_revoked · 429 slow down

files.sign_url

Get a download link — Decides whether a private object may be handed out, and for how long.

Needs files:read · changes data

POSThttps://app.getera.io/api/v1/files/{id}/actions/sign_url
API key or OAuth token · files:read
  • Canonical address: POST /api/v1/actions/files.sign_url

Input

FieldTypeRequiredNotes
iduuidyesFilled from the path {id} · File · id of file
ttlSecondsnumberValid for (seconds)

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
not_foundThe file must still exist in this workspace.
is_folderA folder cannot be downloaded.
no_objectThe entry must have a stored file behind it.
ttl_too_longA download link can last at most 24 hours.

Events it publishes: files.url_signed

What it writes: file (update) — Counts the download

curl -X POST https://app.getera.io/api/v1/files/FILE_ID/actions/sign_url \
  -H "Authorization: Bearer $ERA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"ttlSeconds":1}'
Response
{
  "ok": true,
  "result": "…",
  "events": [
    "files.url_signed"
  ]
}
import { createEra } from "@getera/sdk"

const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.files.signUrl("FILE_ID", {
  ttlSeconds: 1
})
era files sign_url FILE_ID \
  --ttl-seconds 1
tools/call
{
  "name": "era__files__sign_url",
  "arguments": {
    "id": "FILE_ID",
    "ttlSeconds": 1
  }
}

Errors: 401 no or expired credential · 403 the credential does not hold files:read · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 not_found, is_folder, no_object, ttl_too_long · 429 slow down

files.sweep

Clean up files — Closes expired or full upload links and queues long-deleted files’ stored objects for removal.

Needs files:manage · changes data

POSThttps://app.getera.io/api/v1/file_upload_links/actions/sweep
API key or OAuth token · files:manage
  • Canonical address: POST /api/v1/actions/files.sweep

Input

This action takes no input.

Events it publishes: files.link_expired, files.swept

What it writes: file_upload_link (update) — Closes links that expired or filled up

curl -X POST https://app.getera.io/api/v1/file_upload_links/actions/sweep \
  -H "Authorization: Bearer $ERA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{}'
Response
{
  "ok": true,
  "result": "…",
  "events": [
    "files.link_expired",
    "files.swept"
  ]
}
import { createEra } from "@getera/sdk"

const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.fileUploadLinks.sweep()
era file_upload_links sweep
tools/call
{
  "name": "era__files__sweep",
  "arguments": {}
}

Errors: 401 no or expired credential · 403 the credential does not hold files:manage · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 429 slow down

files.unpublish

Make a file private — Stops serving a file publicly; it can then only leave through a signed link.

Needs files:manage · reaches outside (sends, publishes or charges)

POSThttps://app.getera.io/api/v1/files/{id}/actions/unpublish
API key or OAuth token · files:manage
  • Canonical address: POST /api/v1/actions/files.unpublish

Input

FieldTypeRequiredNotes
iduuidyesFilled from the path {id} · File · id of file

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
not_foundThe file must still exist in this workspace.
is_folderA folder cannot be published.
already_privateThe file must be public.

Events it publishes: files.unpublished

What it writes: file (update) — Clears is_public

curl -X POST https://app.getera.io/api/v1/files/FILE_ID/actions/unpublish \
  -H "Authorization: Bearer $ERA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{}'
Response
{
  "ok": true,
  "result": "…",
  "events": [
    "files.unpublished"
  ]
}
import { createEra } from "@getera/sdk"

const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.files.unpublish("FILE_ID")
era files unpublish FILE_ID
tools/call
{
  "name": "era__files__unpublish",
  "arguments": {
    "id": "FILE_ID"
  }
}

Errors: 401 no or expired credential · 403 the credential does not hold files:manage · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 not_found, is_folder, already_private · 429 slow down

files.update

Edit file details — Changes the display name, alt text, description or category of a file.

Needs files:write · changes data

POSThttps://app.getera.io/api/v1/files/{id}/actions/update
API key or OAuth token · files:write
  • REST alias: PATCH /api/v1/files/{id}
  • Canonical address: POST /api/v1/actions/files.update

Input

FieldTypeRequiredNotes
iduuidyesFilled from the path {id} · File · id of file
originalNamestringName · max 500
altTextstringAlt text · max 2000
descriptionstringDescription · max 4000
categorystringCategory · max 255

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
not_foundThe file must still exist in this workspace.

Events it publishes: files.updated

What it writes: file (update) — Updates the editable metadata

curl -X POST https://app.getera.io/api/v1/files/FILE_ID/actions/update \
  -H "Authorization: Bearer $ERA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"originalName":"Name"}'
Response
{
  "ok": true,
  "result": "…",
  "events": [
    "files.updated"
  ]
}
import { createEra } from "@getera/sdk"

const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.files.update("FILE_ID", {
  originalName: "Name"
})
era files update FILE_ID \
  --original-name "Name"
tools/call
{
  "name": "era__files__update",
  "arguments": {
    "id": "FILE_ID",
    "originalName": "Name"
  }
}

Errors: 401 no or expired credential · 403 the credential does not hold files:write · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 not_found · 429 slow down

files.upload

Record an uploaded file — Registers a stored object against a folder, enforcing the size, type and link limits.

Needs files:write · changes data

POSThttps://app.getera.io/api/v1/files/actions/upload
API key or OAuth token · files:write
  • Canonical address: POST /api/v1/actions/files.upload

Input

FieldTypeRequiredNotes
originalNamestringyesFile name · max 500
storedNamestringyesStored name · max 500
storageKeystringyesObject key · max 1024
fileUrlstringReference URL · max 2048
mimeTypestringContent type · max 255
fileTypestring (enum)Kind · Allowed values: image, document, video, audio, other
fileSizenumberyesSize in bytes · min 0
folderPathstringFolder · max 500
categorystringCategory · max 255
agentIdstringProduced by agent · max 64
sourceConnectorstringSource connector · max 64
artifactKindstringArtifact kind · max 24
uploadTokenstringUpload link token · max 64
contextstring (enum)Stored by · Allowed values: file, workspace, knowledge, execution, chat, copilot
executionIdstringWorkflow run · max 128

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
file_too_largeThe file must be within the workspace or link size limit.
type_not_allowedThe link must accept this kind of file.
link_unknownThe upload link must exist.
link_revokedThe upload link must not have been closed.
link_expiredThe upload link must not have expired.
link_fullThe upload link must not have reached its file limit.
storage_quota_exceededThe organization must have room left on its plan.

Events it publishes: files.uploaded

What it writes: file (insert) — Records the file and any folder it needed; file_upload_link (update) — Counts the upload against the link

curl -X POST https://app.getera.io/api/v1/files/actions/upload \
  -H "Authorization: Bearer $ERA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"originalName":"File name","storedName":"Stored name","storageKey":"Object key","fileSize":1}'
Response
{
  "ok": true,
  "result": "…",
  "events": [
    "files.uploaded"
  ]
}
import { createEra } from "@getera/sdk"

const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.files.upload({
  originalName: "File name",
  storedName: "Stored name",
  storageKey: "Object key",
  fileSize: 1
})
era files upload \
  --original-name "File name" \
  --stored-name "Stored name" \
  --storage-key "Object key" \
  --file-size 1
tools/call
{
  "name": "era__files__upload",
  "arguments": {
    "originalName": "File name",
    "storedName": "Stored name",
    "storageKey": "Object key",
    "fileSize": 1
  }
}

Errors: 401 no or expired credential · 403 the credential does not hold files:write · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 file_too_large, type_not_allowed, link_unknown, link_revoked, link_expired, link_full, storage_quota_exceeded · 429 slow down

Data models

Table file_link · Resource file_links

  • GET /api/v1/file_links
  • GET /api/v1/file_links/{id}
ColumnTypeNullableAllowed valuesReferences
idstring (uuid) · uuid
organization_idstring (uuid) · uuidorganization
workspace_idstring (uuid) · uuidworkspace
file_idstring (uuid) · uuidfile
record_typestring · text
record_idstring (uuid) · uuid
linked_by_user_idstring (uuid) · uuidyesapp_user
created_atstring (date-time) · timestamptz

Primary key: id

Unique: file_id, record_type, record_id

Table file_upload_link · Resource file_upload_links

  • GET /api/v1/file_upload_links
  • GET /api/v1/file_upload_links/{id}
ColumnTypeNullableAllowed valuesReferences
idstring (uuid) · uuid
organization_idstring (uuid) · uuidorganization
workspace_idstring (uuid) · uuidworkspace
labelstring · textyes
descriptionstring · textyes
target_folderstring · text
max_filesinteger · int4
max_file_size_mbinteger · int4
allowed_file_typesstring[] · text[]
expires_atstring (date-time) · timestamptzyes
is_activeboolean · bool
upload_countinteger · int4
owner_user_idstring (uuid) · uuidyesapp_user
created_atstring (date-time) · timestamptz
updated_atstring (date-time) · timestamptz

Primary key: id

Unique: token

Checks:

  • CHECK ((max_file_size_mb > 0))
  • CHECK ((max_files > 0))
  • CHECK ((upload_count >= 0))

File

Table file · Resource files

  • GET /api/v1/files
  • GET /api/v1/files/{id}
  • PATCH /api/v1/files/{id} → files.update
ColumnTypeNullableAllowed valuesReferences
idstring (uuid) · uuid
organization_idstring (uuid) · uuidorganization
workspace_idstring (uuid) · uuidworkspace
namestring · text
original_namestring · text
storage_keystring · textyes
file_urlstring · textyes
file_typestring · textimage, document, video, audio, other
mime_typestring · textyes
file_sizestring (int64) · int8yes
folder_pathstring · text
parent_idstring (uuid) · uuidyesfile
categorystring · textyes
alt_textstring · textyes
descriptionstring · textyes
is_folderboolean · bool
is_publicboolean · bool
sort_orderinteger · int4
download_countinteger · int4
owner_user_idstring (uuid) · uuidyesapp_user
agent_idstring · textyes
source_connectorstring · textyes
artifact_kindstring · textyes
versioninteger · int4
upload_link_idstring (uuid) · uuidyesfile_upload_link
deleted_atstring (date-time) · timestamptzyes
created_atstring (date-time) · timestamptz
updated_atstring (date-time) · timestamptz
contextstring · textfile, workspace, knowledge, execution, chat, copilot
execution_idstring · textyes

Primary key: id

Checks:

  • CHECK (((file_size IS NULL) OR (file_size >= 0)))

Events

Workflows and webhooks react to these events.

EventWhat happensPayload
files.attachedA file was attached to a record.id, recordType, recordId
files.deletedA file was deleted.id, name
files.detachedA file was taken off a record.id, recordType, recordId
files.folder_createdA folder was created.id, name, folderPath
files.folder_deletedA folder and its contents were deleted.id, name, count
files.link_createdAn upload link was opened.id, label
files.link_expiredAn upload link was closed because it expired or filled up.id
files.link_renewedAn upload link was given more time.id, expiresAt
files.link_revokedAn upload link was closed.id
files.publishedA file became a public asset.id, name
files.replacedA file’s contents were overwritten.id, name, size
files.sweptThe daily file clean-up ran.linksClosed, objectsQueued, days
files.unpublishedA public file was made private.id, name
files.updatedA file’s details were edited.id, name
files.uploadedA file was stored.id, name, folderPath, size
files.url_signedA download link was handed out for a private file.id, name, ttlSeconds
Was this page helpful?