assets

Stable. Describes behaviour that is built and covered by tests in this repository.

API reference

POST /v1/assets/uploads #

Open a direct multipart upload session for up to 50 files

Returns, for each file, presigned S3 UploadPart URLs for its first (up to 100) parts. Upload the parts in parallel straight to S3 (PUT, each part part_size bytes except the last), keep the ETag of each answer, then call complete. The session lasts 24 hours. Idempotency-Key makes retries return the same session.

Request body

FieldTypeDescription
files requiredarray of CreateUploadSessionFile
Example
{
  "files": [
    {
      "filename": "string",
      "content_type": "application/pdf",
      "size": 1,
      "purpose": "invoice_logo"
    }
  ]
}

Responses

StatusMeaningBody
201CreatedUploadSession
400Error envelopeError
409Error envelopeError
Example response
{
  "id": "string",
  "status": "OPEN",
  "expire_time": "2026-11-03T10:15:00Z",
  "files": [
    {
      "asset_id": "string",
      "filename": "string",
      "status": "string",
      "reject_reason": "string",
      "size": "string",
      "part_size": "string",
      "part_count": 1,
      "presigned_parts": [
        {
          "part_number": 1,
          "url": "string"
        }
      ],
      "uploaded_parts": [
        {
          "part_number": 1,
          "size": "string",
          "etag": "string"
        }
      ]
    }
  ]
}
curl
curl -X POST https://YOUR-API-ADDRESS/v1/assets/uploads \
  -H "Authorization: Bearer $PAYRUDDER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"files":[{"filename":"string","content_type":"application/pdf","size":1,"purpose":"invoice_logo"}]}'

DELETE /v1/assets/uploads/{session} #

Abort a session and every unfinished file in it

Parameters

NameInTypeDescription
session requiredpathstring · uuid

Responses

StatusMeaningBody
200OKUploadSession
404Error envelopeError
Example response
{
  "id": "string",
  "status": "OPEN",
  "expire_time": "2026-11-03T10:15:00Z",
  "files": [
    {
      "asset_id": "string",
      "filename": "string",
      "status": "string",
      "reject_reason": "string",
      "size": "string",
      "part_size": "string",
      "part_count": 1,
      "presigned_parts": [
        {
          "part_number": 1,
          "url": "string"
        }
      ],
      "uploaded_parts": [
        {
          "part_number": 1,
          "size": "string",
          "etag": "string"
        }
      ]
    }
  ]
}
curl
curl -X DELETE https://YOUR-API-ADDRESS/v1/assets/uploads/<session> \
  -H "Authorization: Bearer $PAYRUDDER_API_KEY"

GET /v1/assets/uploads/{session}/files/{asset} #

State of one file, including the parts S3 already holds (to resume an interrupted upload)

Parameters

NameInTypeDescription
session requiredpathstring · uuid
asset requiredpathstring · uuid

Responses

StatusMeaningBody
200OKUploadFile
404Error envelopeError
Example response
{
  "asset_id": "string",
  "filename": "string",
  "status": "string",
  "reject_reason": "string",
  "size": "string",
  "part_size": "string",
  "part_count": 1,
  "presigned_parts": [
    {
      "part_number": 1,
      "url": "string"
    }
  ],
  "uploaded_parts": [
    {
      "part_number": 1,
      "size": "string",
      "etag": "string"
    }
  ]
}
curl
curl -X GET https://YOUR-API-ADDRESS/v1/assets/uploads/<session>/files/<asset> \
  -H "Authorization: Bearer $PAYRUDDER_API_KEY"

DELETE /v1/assets/uploads/{session}/files/{asset} #

Abort one file of a session

Parameters

NameInTypeDescription
session requiredpathstring · uuid
asset requiredpathstring · uuid

Responses

StatusMeaningBody
200OKUploadSession
404Error envelopeError
Example response
{
  "id": "string",
  "status": "OPEN",
  "expire_time": "2026-11-03T10:15:00Z",
  "files": [
    {
      "asset_id": "string",
      "filename": "string",
      "status": "string",
      "reject_reason": "string",
      "size": "string",
      "part_size": "string",
      "part_count": 1,
      "presigned_parts": [
        {
          "part_number": 1,
          "url": "string"
        }
      ],
      "uploaded_parts": [
        {
          "part_number": 1,
          "size": "string",
          "etag": "string"
        }
      ]
    }
  ]
}
curl
curl -X DELETE https://YOUR-API-ADDRESS/v1/assets/uploads/<session>/files/<asset> \
  -H "Authorization: Bearer $PAYRUDDER_API_KEY"

POST /v1/assets/uploads/{session}/files/{asset}/parts #

More (or refreshed) presigned part URLs of a file

Parameters

NameInTypeDescription
session requiredpathstring · uuid
asset requiredpathstring · uuid
fromqueryinteger
countqueryinteger

Responses

StatusMeaningBody
200OKPresignPartsResponse
404Error envelopeError
409Error envelopeError
Example response
{
  "parts": [
    {
      "part_number": 1,
      "url": "string"
    }
  ]
}
curl
curl -X POST https://YOUR-API-ADDRESS/v1/assets/uploads/<session>/files/<asset>/parts \
  -H "Authorization: Bearer $PAYRUDDER_API_KEY"

POST /v1/assets/uploads/{session}/files/{asset}/complete #

Verify the uploaded parts, assemble the object and make the asset READY

Idempotent. If the parts do not match what S3 holds (a missing part or a wrong ETag), the asset comes back REJECTED with reject_reason: PART_MISMATCH and nothing is left in storage.

Parameters

NameInTypeDescription
session requiredpathstring · uuid
asset requiredpathstring · uuid

Request body

FieldTypeDescription
parts requiredarray of CompleteUploadFilePart
Example
{
  "parts": [
    {
      "part_number": 1,
      "etag": "string"
    }
  ]
}

Responses

StatusMeaningBody
200OKAsset
404Error envelopeError
409Error envelopeError
Example response
{
  "id": "string",
  "session_id": "string",
  "purpose": "PURPOSE_INVOICE_LOGO",
  "filename": "string",
  "content_type": "string",
  "size_bytes": "string",
  "sha256": "string",
  "status": "ASSET_STATUS_PENDING",
  "reject_reason": "PART_MISMATCH",
  "create_time": "2026-11-03T10:15:00Z",
  "complete_time": "2026-11-03T10:15:00Z"
}
curl
curl -X POST https://YOUR-API-ADDRESS/v1/assets/uploads/<session>/files/<asset>/complete \
  -H "Authorization: Bearer $PAYRUDDER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"parts":[{"part_number":1,"etag":"string"}]}'

GET /v1/assets #

List READY assets

Parameters

NameInTypeDescription
purposequeryinvoice_logo | invoice_attachment | general
page_sizequeryinteger
page_tokenquerystring

Responses

StatusMeaningBody
200OKListAssetsResponse
400Error envelopeError
Example response
{
  "assets": [
    {
      "id": "string",
      "session_id": "string",
      "purpose": "PURPOSE_INVOICE_LOGO",
      "filename": "string",
      "content_type": "string",
      "size_bytes": "string",
      "sha256": "string",
      "status": "ASSET_STATUS_PENDING",
      "reject_reason": "PART_MISMATCH",
      "create_time": "2026-11-03T10:15:00Z",
      "complete_time": "2026-11-03T10:15:00Z"
    }
  ],
  "next_page_token": "string"
}
curl
curl -X GET https://YOUR-API-ADDRESS/v1/assets \
  -H "Authorization: Bearer $PAYRUDDER_API_KEY"

POST /v1/assets #

Upload up to 20 small files in one multipart/form-data request

Files are streamed through the gateway into storage (at most 25 MiB each, 100 MiB per request); use an upload session for larger files. An optional first part metadata picks each file's purpose: {"files": {"logo.png": {"purpose": "invoice_logo"}}} (default general). The answer is 200 with one result per file, in order: a bad file is rejected with an error_code and does not fail the others. The write rate limit is charged per file.

Responses

StatusMeaningBody
200Per-file resultsUploadAssetsResponse
400Error envelopeError
413Error envelopeError
429Error envelopeError
Example response
{
  "results": [
    {
      "filename": "string",
      "status": "created"
    }
  ]
}
curl
curl -X POST https://YOUR-API-ADDRESS/v1/assets \
  -H "Authorization: Bearer $PAYRUDDER_API_KEY"

GET /v1/assets/{id} #

One asset

Parameters

NameInTypeDescription
id requiredpathstring · uuid

Responses

StatusMeaningBody
200OKAsset
404Error envelopeError
Example response
{
  "id": "string",
  "session_id": "string",
  "purpose": "PURPOSE_INVOICE_LOGO",
  "filename": "string",
  "content_type": "string",
  "size_bytes": "string",
  "sha256": "string",
  "status": "ASSET_STATUS_PENDING",
  "reject_reason": "PART_MISMATCH",
  "create_time": "2026-11-03T10:15:00Z",
  "complete_time": "2026-11-03T10:15:00Z"
}
curl
curl -X GET https://YOUR-API-ADDRESS/v1/assets/<id> \
  -H "Authorization: Bearer $PAYRUDDER_API_KEY"

DELETE /v1/assets/{id} #

Delete a READY asset (its bytes leave the storage quota)

Parameters

NameInTypeDescription
id requiredpathstring · uuid

Responses

StatusMeaningBody
200OKAsset
404Error envelopeError
409Error envelopeError
Example response
{
  "id": "string",
  "session_id": "string",
  "purpose": "PURPOSE_INVOICE_LOGO",
  "filename": "string",
  "content_type": "string",
  "size_bytes": "string",
  "sha256": "string",
  "status": "ASSET_STATUS_PENDING",
  "reject_reason": "PART_MISMATCH",
  "create_time": "2026-11-03T10:15:00Z",
  "complete_time": "2026-11-03T10:15:00Z"
}
curl
curl -X DELETE https://YOUR-API-ADDRESS/v1/assets/<id> \
  -H "Authorization: Bearer $PAYRUDDER_API_KEY"

GET /v1/assets/{id}/download #

A presigned link (5 minutes) that downloads the file as an attachment

Parameters

NameInTypeDescription
id requiredpathstring · uuid

Responses

StatusMeaningBody
200OKSignedUrl
404Error envelopeError
409Error envelopeError
Example response
{
  "url": "string",
  "expires_at": "2026-11-03T10:15:00Z"
}
curl
curl -X GET https://YOUR-API-ADDRESS/v1/assets/<id>/download \
  -H "Authorization: Bearer $PAYRUDDER_API_KEY"

Models

Asset

FieldTypeDescription
id requiredstring
session_id requiredstring
purpose requiredPURPOSE_INVOICE_LOGO | PURPOSE_INVOICE_ATTACHMENT | PURPOSE_GENERAL
filename requiredstring
content_type requiredstring
size_bytes requiredstring · int64
sha256 requiredstring
status requiredASSET_STATUS_PENDING | ASSET_STATUS_UPLOADING | ASSET_STATUS_SCANNING | ASSET_STATUS_READY | ASSET_STATUS_REJECTED | ASSET_STATUS_ABORTED | ASSET_STATUS_CORRUPT | ASSET_STATUS_DELETED
reject_reason requiredstring
create_time requiredstring · date-time
complete_time requiredstring · date-time

CompleteUploadFilePart

FieldTypeDescription
part_number requiredinteger
etag requiredstring

CompleteUploadFileRequest

FieldTypeDescription
parts requiredarray of CompleteUploadFilePart

CreateUploadSessionFile

FieldTypeDescription
filename requiredstring
content_type requiredstring
size requiredinteger
purpose requiredinvoice_logo | invoice_attachment | general
sha256stringOptional hex sha256 of the whole file

CreateUploadSessionRequest

FieldTypeDescription
files requiredarray of CreateUploadSessionFile

Error

FieldTypeDescription
error requiredobject

ListAssetsResponse

FieldTypeDescription
assets requiredarray of Asset
next_page_token requiredstring

PresignPartsResponse

FieldTypeDescription
parts requiredarray of PresignedPart

PresignedPart

FieldTypeDescription
part_number requiredinteger
url requiredstring · uri

SignedUrl

FieldTypeDescription
url requiredstring · uri
expires_at requiredstring · date-timeRFC 3339, UTC

UploadAssetsResponse

FieldTypeDescription
results requiredarray of UploadAssetsResult

UploadAssetsResult

FieldTypeDescription
filename requiredstring
status requiredcreated | rejected
assetAsset
error_codestring
messagestring

UploadFile

FieldTypeDescription
asset_id requiredstring
filename requiredstring
status requiredstring
reject_reason requiredstring
size requiredstring · int64
part_size requiredstring · int64
part_count requiredinteger
presigned_parts requiredarray of PresignedPart
uploaded_parts requiredarray of object

UploadSession

FieldTypeDescription
id requiredstring
status requiredOPEN | COMPLETED | EXPIRED | ABORTED
expire_time requiredstring · date-time
files requiredarray of UploadFile