Skip to content

For developers

PODFY Open API reference

Fetch records, update their status and manage your reference register from your own systems. A JSON REST API, part of PODFY Advanced.

What the API is

The PODFY Open API lets your own systems read records (proofs of delivery and QR reports), update their work status and manage the reference (asset) register. It is a JSON REST API for server to server use. There is no CORS: call it from your backend, never from a browser.

The Open API is part of PODFY Advanced. When it is not active for your brand, every call answers 403 feature_not_active. The specification is OpenAPI 3.1 and the reference below is generated from the same file.

Get an API key

A brand admin creates keys in the portal under Settings → Integrations → API keys. Give the key a name and choose its scopes. The key is shown once, so store it in your secrets manager. You can revoke a key at any time, and a revoked key stops working immediately.

  • records:read: read records
  • records:write: update records (also allows reading them)
  • references:read: read the reference register
  • references:write: create and change references (also allows reading them)

Base URL and authentication

The base URL is https://portal.podfy.net/api/v1. Send your key as a Bearer token in the Authorization header on every request. A key never works in a query parameter.

A missing, malformed, unknown or revoked key always gets the same answer, 401 invalid_key. A key without the required scope gets 403 insufficient_scope.

curl -H "Authorization: Bearer $PODFY_API_KEY" \
  "https://portal.podfy.net/api/v1/records?limit=10"

Live and test keys

Keys starting with pk_live_ see only production records. Keys starting with pk_test_ (QA records only) see only test records from qa.podfy.app, whose PODFY IDs start with QA-. The two environments never mix.

The reference register is shared with production, so test keys cannot create or change references (403 insufficient_scope). A record or reference of another brand, or a record of the other environment, answers 404 not_found, never 403.

Rate limit

Each key may make 600 requests per minute, counted in fixed one-minute windows. Every answer carries X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset (unix seconds when the window ends).

Above the limit you get 429 rate_limited with a Retry-After header (seconds). Wait that long and try again.

Errors

Every error is JSON in the same shape: {"error":{"code":"…","message":"…"}}. Handle errors by the HTTP status and the code, not by the message text. Responses are never cached (Cache-Control: no-store). The full list of codes is in the reference below.

Pagination

List endpoints return {"data": [...], "next_cursor": "…"}. Pass next_cursor back as the cursor parameter to get the next page, until it is null. Cursors are opaque: do not build or change them. Records are listed newest first. The page size is set with limit (1 to 200, default 50).

Reference

Operations

The reference below is generated from the OpenAPI file and is in English in every language. Download the specification: openapi.json

Error codes

StatusCodesMeaning
400invalid_parameter invalid_cursor invalid_body invalid_status invalid_priority invalid_assignee invalid_comment comment_too_long nothing_to_update invalid_ref name_too_long location_too_long group_code_too_long sticker_text_too_long no_valid_fieldsInvalid query parameter or body.
401invalid_keyThe API key is missing, malformed, unknown or revoked (always the same body).
403feature_not_active insufficient_scopeThe Open API (or, for references, the reference register; for record updates, status tracking) is not active for the brand, or the key lacks the scope.
404not_foundNo such endpoint, or no such record / reference for this key's brand and environment.
405method_not_allowedThe endpoint exists but not with this method; Allow lists the methods.
409ref_existsThe reference already exists.
429rate_limitedMore than 600 requests in this minute for this key.
500server_errorUnexpected error; try again later.

GET /records

List records Required scope: records:read

Records of your brand, newest first. Deleted records are never listed.

Parameters

NameInTypeRequiredDescription
sincequerystringoptionalOnly records created at or after this moment (ISO 8601 date or date-time; a time without zone is UTC).
updated_sincequerystringoptionalOnly records changed (or created) at or after this moment (ISO 8601).
statusquerystringoptionalWork status, one or more comma separated.
referencequerystring max 100 charactersoptionalExact reference (1-100 characters).
categoryquerystring max 40 charactersoptionalCategory code (letters, digits, _ and -; case-insensitive).
limitqueryinteger 1-200 default 50optionalPage size, 1-200 (default 50; larger values are capped at 200).
cursorquerystringoptionalThe next_cursor of the previous page (opaque).

Response 200

A page of records.

FieldTypeRequiredDescription
dataarray of objectrequired
data[].podfy_idstringrequired
data[].modestring one of delivery, reportrequired
data[].referencestringoptionalEmpty string when none.
data[].assetobjectoptionalRegister data at upload time.
data[].asset.namestring or nulloptional
data[].asset.locationstring or nulloptional
data[].asset.groupstring or nulloptional
data[].categorystring or nulloptionalCategory code (report mode) or delivery issue code.
data[].prioritystring one of low, normal, high, criticalrequired
data[].work_statusstring one of new, in_progress, resolved, rejectedrequired
data[].notestringoptional
data[].fieldsobjectoptionalCustom form fields.
data[].group_idstringrequiredRecords of one multi-file upload share it (the podfy_id without the -N suffix).
data[].filesarray of objectrequired
data[].files[].namestringoptional
data[].files[].urlstring urioptional
data[].locationobject or nulloptional
data[].location.latnumberoptional
data[].location.lngnumberoptional
data[].location.sourcestring or nulloptional
data[].duplicate_ofstring or nulloptionalpodfy_id of the first report this one duplicates.
data[].urlstring or null urioptionalPublic detail page of the record.
data[].assigneestring or nulloptionalE-mail of the assigned brand member.
data[].created_atstring date-timerequired
data[].updated_atstring or null date-timeoptional
data[].resolved_atstring or null date-timeoptional
data[].keep_informedbooleanoptionalTrue when the reporter asked to be kept informed (a confirmation or resolved message was or can be sent). Never the address.
next_cursorstring or nullrequired

Errors

StatusCodesMeaning
400invalid_parameter invalid_cursor invalid_body invalid_status invalid_priority invalid_assignee invalid_comment comment_too_long nothing_to_update invalid_ref name_too_long location_too_long group_code_too_long sticker_text_too_long no_valid_fieldsInvalid query parameter or body.
401invalid_keyThe API key is missing, malformed, unknown or revoked (always the same body).
403feature_not_active insufficient_scopeThe Open API (or, for references, the reference register; for record updates, status tracking) is not active for the brand, or the key lacks the scope.
404not_foundNo such endpoint, or no such record / reference for this key's brand and environment.
405method_not_allowedThe endpoint exists but not with this method; Allow lists the methods.
429rate_limitedMore than 600 requests in this minute for this key.
500server_errorUnexpected error; try again later.

Example

curl \
  -H "Authorization: Bearer $PODFY_API_KEY" \
  "https://portal.podfy.net/api/v1/records?limit=50"

GET /records/{podfy_id}

Get a record Required scope: records:read

One record with its timeline (created, status, comment, assign, priority, duplicate). Mail recipients and reporter data never appear.

Parameters

NameInTypeRequiredDescription
podfy_idpathstring pattern ^[A-Za-z0-9_-]{1,64}$requiredThe record's PODFY ID, e.g. PFY-7Q4K2-1 (QA-… for test records).

Response 200

The record.

FieldTypeRequiredDescription
podfy_idstringrequired
modestring one of delivery, reportrequired
referencestringoptionalEmpty string when none.
assetobjectoptionalRegister data at upload time.
asset.namestring or nulloptional
asset.locationstring or nulloptional
asset.groupstring or nulloptional
categorystring or nulloptionalCategory code (report mode) or delivery issue code.
prioritystring one of low, normal, high, criticalrequired
work_statusstring one of new, in_progress, resolved, rejectedrequired
notestringoptional
fieldsobjectoptionalCustom form fields.
group_idstringrequiredRecords of one multi-file upload share it (the podfy_id without the -N suffix).
filesarray of objectrequired
files[].namestringoptional
files[].urlstring urioptional
locationobject or nulloptional
location.latnumberoptional
location.lngnumberoptional
location.sourcestring or nulloptional
duplicate_ofstring or nulloptionalpodfy_id of the first report this one duplicates.
urlstring or null urioptionalPublic detail page of the record.
assigneestring or nulloptionalE-mail of the assigned brand member.
created_atstring date-timerequired
updated_atstring or null date-timeoptional
resolved_atstring or null date-timeoptional
keep_informedbooleanoptionalTrue when the reporter asked to be kept informed (a confirmation or resolved message was or can be sent). Never the address.
eventsarray of objectrequired
events[].typestring one of created, status, comment, assign, priority, duplicaterequired
events[].fromstring or nulloptional
events[].tostring or nulloptional
events[].notestring or nulloptionalThe comment text (comment events only).
events[].actorstring or nulloptionalBrand member e-mail, app, or api:<key prefix>.
events[].created_atstring date-timerequired

Errors

StatusCodesMeaning
400invalid_parameter invalid_cursor invalid_body invalid_status invalid_priority invalid_assignee invalid_comment comment_too_long nothing_to_update invalid_ref name_too_long location_too_long group_code_too_long sticker_text_too_long no_valid_fieldsInvalid query parameter or body.
401invalid_keyThe API key is missing, malformed, unknown or revoked (always the same body).
403feature_not_active insufficient_scopeThe Open API (or, for references, the reference register; for record updates, status tracking) is not active for the brand, or the key lacks the scope.
404not_foundNo such endpoint, or no such record / reference for this key's brand and environment.
405method_not_allowedThe endpoint exists but not with this method; Allow lists the methods.
429rate_limitedMore than 600 requests in this minute for this key.
500server_errorUnexpected error; try again later.

Example

curl \
  -H "Authorization: Bearer $PODFY_API_KEY" \
  "https://portal.podfy.net/api/v1/records/PFY-7Q4K2-1"

PATCH /records/{podfy_id}

Update a record Required scope: records:write

Change the work status, assignee or priority, or add a comment. Same rules as the portal: one timeline event per change, webhooks record.status_changed / record.updated are sent, the actor is api:<key prefix>. Needs status tracking (report mode or the status tracking add-on), else 403 feature_not_active. Only the fields below are accepted.

Parameters

NameInTypeRequiredDescription
podfy_idpathstring pattern ^[A-Za-z0-9_-]{1,64}$requiredThe record's PODFY ID, e.g. PFY-7Q4K2-1 (QA-… for test records).

Request body (JSON)

FieldTypeRequiredDescription
work_statusstring one of new, in_progress, resolved, rejectedoptional
assigneestring or null emailoptionalAn active member of the brand, or null to unassign.
prioritystring one of low, normal, high, criticaloptional
commentstring or null max 2000 charactersoptionalAdded to the timeline.

Response 200

The record after the update. changed is false when nothing differed.

FieldTypeRequiredDescription
changedbooleanrequired
recordobjectrequiredThe same record object as in webhook payloads, plus timestamps and the assignee.
record.podfy_idstringrequired
record.modestring one of delivery, reportrequired
record.referencestringoptionalEmpty string when none.
record.assetobjectoptionalRegister data at upload time.
record.asset.namestring or nulloptional
record.asset.locationstring or nulloptional
record.asset.groupstring or nulloptional
record.categorystring or nulloptionalCategory code (report mode) or delivery issue code.
record.prioritystring one of low, normal, high, criticalrequired
record.work_statusstring one of new, in_progress, resolved, rejectedrequired
record.notestringoptional
record.fieldsobjectoptionalCustom form fields.
record.group_idstringrequiredRecords of one multi-file upload share it (the podfy_id without the -N suffix).
record.filesarray of objectrequired
record.files[].namestringoptional
record.files[].urlstring urioptional
record.locationobject or nulloptional
record.location.latnumberoptional
record.location.lngnumberoptional
record.location.sourcestring or nulloptional
record.duplicate_ofstring or nulloptionalpodfy_id of the first report this one duplicates.
record.urlstring or null urioptionalPublic detail page of the record.
record.assigneestring or nulloptionalE-mail of the assigned brand member.
record.created_atstring date-timerequired
record.updated_atstring or null date-timeoptional
record.resolved_atstring or null date-timeoptional
record.keep_informedbooleanoptionalTrue when the reporter asked to be kept informed (a confirmation or resolved message was or can be sent). Never the address.

Errors

StatusCodesMeaning
400invalid_parameter invalid_cursor invalid_body invalid_status invalid_priority invalid_assignee invalid_comment comment_too_long nothing_to_update invalid_ref name_too_long location_too_long group_code_too_long sticker_text_too_long no_valid_fieldsInvalid query parameter or body.
401invalid_keyThe API key is missing, malformed, unknown or revoked (always the same body).
403feature_not_active insufficient_scopeThe Open API (or, for references, the reference register; for record updates, status tracking) is not active for the brand, or the key lacks the scope.
404not_foundNo such endpoint, or no such record / reference for this key's brand and environment.
405method_not_allowedThe endpoint exists but not with this method; Allow lists the methods.
429rate_limitedMore than 600 requests in this minute for this key.
500server_errorUnexpected error; try again later.

Example

curl -X PATCH \
  -H "Authorization: Bearer $PODFY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"work_status": "in_progress", "assignee": "[email protected]"}' \
  "https://portal.podfy.net/api/v1/records/PFY-7Q4K2-1"

GET /references

List references Required scope: references:read

Your reference register, ordered by ref.

Parameters

NameInTypeRequiredDescription
statusquerystring one of active, retiredoptional
groupquerystring max 40 charactersoptionalExact group code.
qquerystring max 100 charactersoptionalText contained in ref, name or location (1-100 characters).
limitqueryinteger 1-200 default 50optionalPage size, 1-200 (default 50; larger values are capped at 200).
cursorquerystringoptionalThe next_cursor of the previous page (opaque).

Response 200

A page of references.

FieldTypeRequiredDescription
dataarray of objectrequired
data[].refstringrequired
data[].kindstring or null one of asset, shipmentoptional
data[].namestring or nulloptional
data[].locationstring or nulloptional
data[].group_codestring or nulloptional
data[].sticker_textstring or nulloptional
data[].statusstring one of active, retiredrequired
data[].active_fromstring or nulloptional
data[].retired_atstring or null date-timeoptional
data[].created_atstring or null date-timeoptional
data[].updated_atstring or null date-timeoptional
data[].qr_urlstring urirequiredhttps://podfy.app/{brand}/{ref}
next_cursorstring or nullrequired

Errors

StatusCodesMeaning
400invalid_parameter invalid_cursor invalid_body invalid_status invalid_priority invalid_assignee invalid_comment comment_too_long nothing_to_update invalid_ref name_too_long location_too_long group_code_too_long sticker_text_too_long no_valid_fieldsInvalid query parameter or body.
401invalid_keyThe API key is missing, malformed, unknown or revoked (always the same body).
403feature_not_active insufficient_scopeThe Open API (or, for references, the reference register; for record updates, status tracking) is not active for the brand, or the key lacks the scope.
404not_foundNo such endpoint, or no such record / reference for this key's brand and environment.
405method_not_allowedThe endpoint exists but not with this method; Allow lists the methods.
429rate_limitedMore than 600 requests in this minute for this key.
500server_errorUnexpected error; try again later.

Example

curl \
  -H "Authorization: Bearer $PODFY_API_KEY" \
  "https://portal.podfy.net/api/v1/references?limit=50"

POST /references

Create a reference Required scope: references:write

Adds one reference. 409 ref_exists when the ref is already in the register.

QA keys (pk_test_) cannot change references: the register is shared with production records (403 insufficient_scope).

Request body (JSON)

FieldTypeRequiredDescription
refstring pattern ^[A-Za-z0-9._-]{1,64}$required
namestring or null max 120 charactersoptional
locationstring or null max 120 charactersoptional
group_codestring or null max 40 charactersoptional
sticker_textstring or null max 120 charactersoptional

Response 201

The new reference.

FieldTypeRequiredDescription
refstringrequired
kindstring or null one of asset, shipmentoptional
namestring or nulloptional
locationstring or nulloptional
group_codestring or nulloptional
sticker_textstring or nulloptional
statusstring one of active, retiredrequired
active_fromstring or nulloptional
retired_atstring or null date-timeoptional
created_atstring or null date-timeoptional
updated_atstring or null date-timeoptional
qr_urlstring urirequiredhttps://podfy.app/{brand}/{ref}

Errors

StatusCodesMeaning
400invalid_parameter invalid_cursor invalid_body invalid_status invalid_priority invalid_assignee invalid_comment comment_too_long nothing_to_update invalid_ref name_too_long location_too_long group_code_too_long sticker_text_too_long no_valid_fieldsInvalid query parameter or body.
401invalid_keyThe API key is missing, malformed, unknown or revoked (always the same body).
403feature_not_active insufficient_scopeThe Open API (or, for references, the reference register; for record updates, status tracking) is not active for the brand, or the key lacks the scope.
404not_foundNo such endpoint, or no such record / reference for this key's brand and environment.
405method_not_allowedThe endpoint exists but not with this method; Allow lists the methods.
409ref_existsThe reference already exists.
429rate_limitedMore than 600 requests in this minute for this key.
500server_errorUnexpected error; try again later.

Example

curl -X POST \
  -H "Authorization: Bearer $PODFY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"ref": "example", "name": "example"}' \
  "https://portal.podfy.net/api/v1/references"

GET /references/{ref}

Get a reference Required scope: references:read

One reference.

Parameters

NameInTypeRequiredDescription
refpathstring pattern ^[A-Za-z0-9._-]{1,64}$requiredThe reference (1-64 characters: letters, digits, dot, underscore, hyphen).

Response 200

The reference.

FieldTypeRequiredDescription
refstringrequired
kindstring or null one of asset, shipmentoptional
namestring or nulloptional
locationstring or nulloptional
group_codestring or nulloptional
sticker_textstring or nulloptional
statusstring one of active, retiredrequired
active_fromstring or nulloptional
retired_atstring or null date-timeoptional
created_atstring or null date-timeoptional
updated_atstring or null date-timeoptional
qr_urlstring urirequiredhttps://podfy.app/{brand}/{ref}

Errors

StatusCodesMeaning
400invalid_parameter invalid_cursor invalid_body invalid_status invalid_priority invalid_assignee invalid_comment comment_too_long nothing_to_update invalid_ref name_too_long location_too_long group_code_too_long sticker_text_too_long no_valid_fieldsInvalid query parameter or body.
401invalid_keyThe API key is missing, malformed, unknown or revoked (always the same body).
403feature_not_active insufficient_scopeThe Open API (or, for references, the reference register; for record updates, status tracking) is not active for the brand, or the key lacks the scope.
404not_foundNo such endpoint, or no such record / reference for this key's brand and environment.
405method_not_allowedThe endpoint exists but not with this method; Allow lists the methods.
429rate_limitedMore than 600 requests in this minute for this key.
500server_errorUnexpected error; try again later.

Example

curl \
  -H "Authorization: Bearer $PODFY_API_KEY" \
  "https://portal.podfy.net/api/v1/references/YOUR_REF"

PATCH /references/{ref}

Update a reference Required scope: references:write

Edit, move, retire (status: retired) or reactivate a reference. Only the fields below are accepted; an empty string clears a text field.

QA keys (pk_test_) cannot change references: the register is shared with production records (403 insufficient_scope).

Parameters

NameInTypeRequiredDescription
refpathstring pattern ^[A-Za-z0-9._-]{1,64}$requiredThe reference (1-64 characters: letters, digits, dot, underscore, hyphen).

Request body (JSON)

FieldTypeRequiredDescription
namestring or null max 120 charactersoptional
locationstring or null max 120 charactersoptional
group_codestring or null max 40 charactersoptional
sticker_textstring or null max 120 charactersoptional
statusstring one of active, retiredoptional

Response 200

The reference after the update.

FieldTypeRequiredDescription
changedbooleanrequired
referenceobjectrequired
reference.refstringrequired
reference.kindstring or null one of asset, shipmentoptional
reference.namestring or nulloptional
reference.locationstring or nulloptional
reference.group_codestring or nulloptional
reference.sticker_textstring or nulloptional
reference.statusstring one of active, retiredrequired
reference.active_fromstring or nulloptional
reference.retired_atstring or null date-timeoptional
reference.created_atstring or null date-timeoptional
reference.updated_atstring or null date-timeoptional
reference.qr_urlstring urirequiredhttps://podfy.app/{brand}/{ref}

Errors

StatusCodesMeaning
400invalid_parameter invalid_cursor invalid_body invalid_status invalid_priority invalid_assignee invalid_comment comment_too_long nothing_to_update invalid_ref name_too_long location_too_long group_code_too_long sticker_text_too_long no_valid_fieldsInvalid query parameter or body.
401invalid_keyThe API key is missing, malformed, unknown or revoked (always the same body).
403feature_not_active insufficient_scopeThe Open API (or, for references, the reference register; for record updates, status tracking) is not active for the brand, or the key lacks the scope.
404not_foundNo such endpoint, or no such record / reference for this key's brand and environment.
405method_not_allowedThe endpoint exists but not with this method; Allow lists the methods.
429rate_limitedMore than 600 requests in this minute for this key.
500server_errorUnexpected error; try again later.

Example

curl -X PATCH \
  -H "Authorization: Bearer $PODFY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name": "example", "location": "example"}' \
  "https://portal.podfy.net/api/v1/references/YOUR_REF"