Token document uploads
Upload, confirm, list, download, and delete token or asset documents through the DALP API, SDK, and CLI.
The token document API manages files attached to an asset: a prospectus, term sheet, regulatory filing, compliance report, certificate, reserve audit, or other supporting file. The API uses the same two-step pattern as other upload flows:
- Request a presigned upload URL for a token document.
- Upload the file directly to storage using the returned method and headers.
- Confirm the upload so DALP records the file against the token.
- List, download, or delete the record later through the token document API.
This flow covers token or asset documents. Use the KYC document upload guide when the file belongs to a user's KYC profile instead of an asset.
Before you start
You need:
- a DALP API key with access to the token document operations.
- the token contract address for the asset that owns the document.
- the asset profile, because DALP validates
documentTypeagainst the profile. - the file name, MIME type, size in bytes, and visibility level before requesting an upload URL.
Flow at a glance
The upload URL and confirm calls are separate because the file bytes go
directly to storage. DALP records the file only after you confirm the
returned objectKey.
If the direct storage upload fails, do not confirm. Request a fresh upload URL
when the returned expiresAt time has passed or when storage rejects the
returned headers.
Endpoints
The token document API exposes these operations:
GET /api/v2/tokens/{tokenAddress}/documentslists token documents with filtering and sorting (paginated).POST /api/v2/tokens/{tokenAddress}/document-uploadsreturns a presigned upload URL.POST /api/v2/tokens/{tokenAddress}/documentsconfirms an uploaded file and creates the document record.POST /api/v2/tokens/{tokenAddress}/documents/{documentId}/downloadsreturns a secure download URL.DELETE /api/v2/tokens/{tokenAddress}/documents/{documentId}deletes a token document.
Request an upload URL
Request an upload URL before sending the file bytes. The request describes the file and how it should be classified on the asset.
const upload = await client.token.documents.getUploadUrl({
params: {
tokenAddress: "0xTOKEN",
},
body: {
documentType: "prospectus",
fileName: "bond-prospectus.pdf",
fileSize: 2_400_000,
mimeType: "application/pdf",
visibility: "public",
title: "Bond prospectus",
description: "Published prospectus for investor review",
},
});The upload URL request accepts these fields:
| Field | Required | Description |
|---|---|---|
documentType | Yes | Token document type. Use a value allowed by the asset profile. |
fileName | Yes | File name, up to 255 characters. |
fileSize | Yes | Integer size in bytes. The value must be positive and no larger than 50 MiB. |
mimeType | Yes | One of the supported MIME types below. |
visibility | Yes | public, holders, or restricted. |
title | No | Display title, up to 500 characters. |
description | No | Description, up to 2,000 characters. |
Choose the document type for the asset profile
documentType is validated against the asset profile, not only against the
shared document-type list. Use the value that matches the asset being filed.
For reserve-backed assets, choose a type that describes the external file or
attestation you are attaching to the token record. The asset profile also accepts
common legal and regulatory types, including compliance filings. See the full
table below.
| Asset profile | Reserve or asset evidence document types |
|---|---|
stablecoin | reserve_audit, attestation_report, reserve_composition, certificate, or other. |
precious-metal | assay_certificate, storage_receipt, chain_of_custody, insurance_certificate, certificate, or other. |
Use the stablecoin values for reserve audits, verifier attestations, or
reserve-composition files. Use the precious-metal values for assay certificates,
storage receipts, custody-chain files, or insurance certificates. For legal,
regulatory, or compliance files, keep the more specific common type when the
asset profile accepts it. Use other only when no accepted value fits, then
set a clear title and description so reviewers can identify the file later.
The API and SDK accept these MIME types on an upload URL request:
application/pdfimage/jpegimage/pngimage/webpapplication/vnd.openxmlformats-officedocument.wordprocessingml.documentapplication/vnd.openxmlformats-officedocument.spreadsheetml.sheettext/csv
The CLI documents get-upload-url and documents confirm-upload commands accept
the same list except text/csv. To upload a CSV document, use the API or SDK.
The upload URL response includes:
| Field | Description |
|---|---|
uploadUrl | Presigned URL for the direct file upload. |
objectKey | Storage object key to send in the confirm request. |
expiresAt | Expiry timestamp for the presigned URL. |
method | HTTP method for the upload. Token documents use PUT. |
headers | Headers that must be sent with the direct storage upload. |
Upload the file bytes
Upload the file directly to the returned URL using the returned method and headers. Some storage backends add provider-specific headers to the URL response.
await fetch(upload.data.uploadUrl, {
method: upload.data.method,
headers: upload.data.headers,
body: fileBytes,
});Do not replace the returned headers with only Content-Type. Provider-specific
headers are part of the upload contract.
Confirm the uploaded file
After the file upload succeeds, confirm it with the returned objectKey.
DALP creates the token document record and returns the stored file metadata.
const document = await client.token.documents.confirmUpload({
params: {
tokenAddress: "0xTOKEN",
},
body: {
objectKey: upload.data.objectKey,
documentType: "prospectus",
fileName: "bond-prospectus.pdf",
fileSize: 2_400_000,
mimeType: "application/pdf",
visibility: "public",
title: "Bond prospectus",
description: "Published prospectus for investor review",
},
});The confirm request repeats the file metadata and adds objectKey. It also
accepts replaceGroupId when the new file replaces an earlier document in the
same version group.
The response includes the document id, groupId, versionNumber, isLatest,
fileHash, uploadedAt, and uploader details.
DALP calculates fileHash from the uploaded bytes when you confirm. Store that
value in downstream systems to verify the asset document still points to the
expected file.
On-chain file hash claims
Upload records and asset-level claims serve different purposes:
- The token document upload flow stores the file metadata, version group, and
fileHashthat belongs to the uploaded file. - An asset-level document-hash claim can record a SHA-256 hash, document type, and file name without placing the file contents on-chain.
Use the upload flow to manage access, download links, and versioning. Use an asset-level document-hash claim when an asset also needs an on-chain hash anchor for a specific file.
Choose document type and visibility
Choose the document type from the token's asset profile, not from the full token catalog. DALP uses the asset type to narrow the choices shown in the Console upload dialog: a stablecoin shows reserve options; a precious metal shows assay, storage, custody-chain, and insurance options.
| Asset profile | Document type choices |
|---|---|
| Bonds | prospectus, term_sheet, legal_opinion, regulatory_filing, compliance_report, annual_report, financial_statement, credit_rating, covenant_agreement, interest_schedule, certificate, other |
| Equity | prospectus, term_sheet, legal_opinion, regulatory_filing, compliance_report, annual_report, financial_statement, shareholder_agreement, certificate, other |
| Funds | prospectus, term_sheet, legal_opinion, regulatory_filing, compliance_report, annual_report, financial_statement, fund_fact_sheet, subscription_agreement, nav_report, certificate, other |
| Stablecoins | legal_opinion, regulatory_filing, compliance_report, reserve_audit, attestation_report, reserve_composition, certificate, other |
| Deposits | term_sheet, legal_opinion, regulatory_filing, compliance_report, financial_statement, interest_schedule, certificate, other |
| Real estate | legal_opinion, regulatory_filing, compliance_report, appraisal, property_deed, survey_report, environmental_assessment, title_insurance, insurance_certificate, certificate, other |
| Precious metals | legal_opinion, regulatory_filing, compliance_report, assay_certificate, storage_receipt, chain_of_custody, insurance_certificate, certificate, other |
For API and CLI integrations, send a documentType value that belongs to the
asset profile you are updating. For example, use reserve_audit,
attestation_report, or reserve_composition for stablecoin reserve files,
and use assay_certificate, storage_receipt, or chain_of_custody for
precious metal files. Use other only when the file does not fit a more
specific type.
The visibility field controls who can access the file:
public: visible to any caller who can reach the token document surface.holders: visible to a caller who holds an operational role on the asset, and to an investor who holds the token. Holding the token means the caller's wallet has a confirmed non-zero balance of the asset. A balance-only holder reads holder-tier documents; they do not gain management rights over them.restricted: visible only to a caller with governance or admin rights on the asset.
DALP confirms a caller's balance from indexed on-chain state. This check only affects the balance-based path: a caller who holds an asset role keeps holder-tier access regardless. If the balance lookup cannot complete, the balance-only path fails closed, so a caller without a role and without a confirmed balance does not receive holder-tier files. This keeps a transient lookup problem from widening access by mistake.
Choose the narrowest value that fits the operating process and regulatory basis for the file. When in doubt, restrict access and widen it later.
List documents
Use the list endpoint to reconcile published files or populate an asset's document table. Call it from a background job or on demand when a user requests the file list for an asset.
curl --globoff "https://your-platform.example.com/api/v2/tokens/0xTOKEN/documents?page[limit]=50&sort=-uploadedAt" \
-H "X-Api-Key: sm_dalp_xxxxxxxxxxxxxxxx"The list endpoint supports sorting, filtering, and facets with standard
pagination. Sort by fileName, documentType, visibility, fileSize, or
uploadedAt. Filter by fileName, documentType, visibility,
mimeType, isLatest, fileSize, or uploadedAt.
Use the standard collection filter shape to narrow results. For example, request the latest public prospectuses uploaded after a cutoff time:
curl --globoff "https://your-platform.example.com/api/v2/tokens/0xTOKEN/documents?filter[documentType][eq]=prospectus&filter[visibility][eq]=public&filter[uploadedAt][gte]=2026-01-01T00:00:00.000Z" \
-H "X-Api-Key: sm_dalp_xxxxxxxxxxxxxxxx"The API returns only the latest non-deleted records visible to the caller. Public files are visible to any caller who can reach the token document surface. Holder-tier files are visible to a caller with an asset role or to an investor who holds the token, confirmed from a non-zero on-chain balance. Restricted files require governance or admin rights. The download endpoint applies the same access rule, so the list never surfaces a file the caller cannot then download.
The list response uses the standard shape with data, meta, and links. Each
row includes the document identifiers, token address, type, access level, version
group fields, file metadata, optional title and description, fileHash,
uploadedAt, uploader details, and thumbnailUrl.
Preview thumbnails
Each list row carries a thumbnailUrl so you can render a visual preview next to
the file without downloading the full document. Use it to build a document
gallery or to show a thumbnail in an asset's document table.
| File type | thumbnailUrl value |
|---|---|
| Presigned URL of a generated preview of the first page. | |
Image (jpeg, png, webp) | Presigned URL of the image itself. |
| Word, spreadsheet, CSV, other | null. Fall back to a type icon in your interface. |
A thumbnail URL is a short-lived presigned link, separate from the full download
URL, and expires one hour after the list response is generated. Re-list to get a
fresh URL rather than caching it. The field is also null when a thumbnail has
not been generated yet or its stored object cannot be read, so always handle the
null case in your interface.
Download or delete
To retrieve a file, request a secure download URL:
const download = await client.token.documents.getDownloadUrl({
params: {
tokenAddress: "0xTOKEN",
documentId: document.data.id,
},
});To remove a file from the asset's record, call delete. Only do this when the file should no longer appear on the API surface:
await client.token.documents.delete({
params: {
tokenAddress: "0xTOKEN",
documentId: document.data.id,
},
});Keep your delete reasoning and approval records in your operating logs. The call removes the file from the API surface but does not replace your regulated record-keeping process.
Error codes
When a document operation fails, the API returns a stable DALP-NNNN error
code. Branch on the code rather than the message text so an automated
integration can decide whether the caller corrects and resends, retries once,
or routes the failure to an operator. Each code below maps to a canonical entry
in the platform API error reference.
The Retry column states whether resending helps. no retry means the caller
must change the request, the credentials, or the uploaded file before the call
will succeed. operator means the caller cannot resolve the failure from the
request alone and should re-prepare the upload or contact the operator.
Confirm the uploaded file
These codes surface on the confirm step, after the file bytes reach storage. They guard the object key, the declared size, the document type, the replacement group, and the record write.
| Code | Category | Retry | When it happens |
|---|---|---|---|
| DALP-0327 | client | no retry | The objectKey does not start with the token's storage prefix or contains path traversal sequences. Send the objectKey from the upload URL response unchanged. |
| DALP-0326 | client | no retry | The platform could not read the uploaded file at the expected location. Re-upload the file through the presigned URL, confirm it completed, then confirm again. |
| DALP-0321 | operational | no retry | The stored object's byte length differs from the fileSize in the confirm request, and the orphaned object was removed. Re-upload and confirm with the exact file size. |
| DALP-0324 | client | no retry | The documentType is not permitted for the token's asset type. This check also runs on the upload URL request, so a bad type can fail there first. Choose a type allowed by the asset profile and resend. |
| DALP-0322 | client | no retry | The replaceGroupId matches no existing document group for this token. The uploaded object was removed. Request a fresh upload URL, re-upload the file, and confirm with the new object key. |
| DALP-0325 | operational | operator | The platform inserted the document metadata but the insert returned no row, indicating a persistence failure, and the orphaned object was removed from storage. Request a fresh upload URL, re-upload the file, and confirm with the new object key. If it recurs, contact support with the request ID. |
Download a document
These codes surface on the download URL request. They enforce the document's visibility level against the caller's token roles and on-chain balance.
| Code | Category | Retry | When it happens |
|---|---|---|---|
| DALP-0323 | client | no retry | The document ID matches no active document for this token. Verify the ID against the token's document list and resend. |
| DALP-0319 | client | no retry | The document has holders visibility, and the caller holds no role on the token and no confirmed on-chain balance of it. Use credentials for a wallet that holds a token role or a positive balance of the asset, or ask an administrator to assign a role. |
| DALP-0320 | operational | no retry | The document has restricted visibility and the caller holds no governance or admin role on the token. Use credentials with a governance or admin role. |
Delete a document
The delete operation reuses the document lookup, so it returns the same not-found code when the target no longer exists.
| Code | Category | Retry | When it happens |
|---|---|---|---|
| DALP-0323 | client | no retry | The document ID matches no active document for this token. The document may have been deleted already, or the ID may belong to a different token. Verify the ID and resend. |
CLI commands
The DALP CLI exposes the same operations:
dalp tokens documents list 0xTOKEN
dalp tokens documents get-upload-url \
--address 0xTOKEN \
--fileName bond-prospectus.pdf \
--fileSize 2400000 \
--mimeType application/pdf \
--documentType prospectus \
--visibility public
dalp tokens documents confirm-upload \
--address 0xTOKEN \
--objectKey uploads/token-documents/example-object-key \
--documentType prospectus \
--fileName bond-prospectus.pdf \
--fileSize 2400000 \
--mimeType application/pdf \
--visibility public
dalp tokens documents get-download-url \
--address 0xTOKEN \
--documentId doc_123
dalp tokens documents delete \
--address 0xTOKEN \
--documentId doc_123Use the API or SDK for the direct file upload step. The CLI returns the presigned URL and object key but does not upload local file bytes.