# Token document uploads

Source: https://docs.settlemint.com/docs/developers/developer-guides/api-integration/token-documents
Upload, confirm, list, download, and delete token or asset documents through the DALP API, SDK, and CLI.



DALP token document APIs manage files that belong to an asset, such as a
prospectus, term sheet, regulatory filing, compliance report, certificate,
reserve audit, or other asset evidence. The API uses the same two-step upload
pattern as other document flows:

1. Request a presigned upload URL for a token document.
2. Upload the file directly to storage using the returned method and headers.
3. Confirm the upload so DALP records the document against the token.
4. List, download, or delete the document later through the token document API.

This flow is for 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 [#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 `documentType` against the profile.
* the file name, MIME type, size in bytes, and visibility level before requesting
  an upload URL.

## Flow at a glance [#flow-at-a-glance]

The upload URL and confirmation calls are separate because the file bytes go
directly to storage. DALP records the token document only after you confirm the
returned `objectKey`.

<Mermaid
  chart="sequenceDiagram
    participant App as Integration app
    participant API as DALP API
    participant Storage as Document storage

    App->>API: POST /api/v2/tokens/{tokenAddress}/document-uploads
    API-->>App: uploadUrl, method, headers, objectKey, expiresAt
    App->>Storage: PUT file bytes with returned headers
    Storage-->>App: Upload accepted
    App->>API: POST /api/v2/tokens/{tokenAddress}/documents with objectKey
    API-->>App: Token document record with id, groupId, fileHash
    App->>API: List, download, or delete document records"
/>

If the direct storage upload fails, do not confirm the document. Request a fresh
upload URL when the returned `expiresAt` time has passed or when storage rejects
the returned headers.

## Endpoint reference [#endpoint-reference]

The token document API exposes these operations:

* `GET /api/v2/tokens/{tokenAddress}/documents` lists token documents with pagination, sorting, and filtering.
* `POST /api/v2/tokens/{tokenAddress}/document-uploads` returns a presigned upload URL.
* `POST /api/v2/tokens/{tokenAddress}/documents` confirms an uploaded file and creates the document record.
* `POST /api/v2/tokens/{tokenAddress}/documents/{documentId}/downloads` returns a secure download URL.
* `DELETE /api/v2/tokens/{tokenAddress}/documents/{documentId}` deletes a token document.

## Request an upload URL [#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.

```ts
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.                                         |

Supported MIME types are:

* `application/pdf`
* `image/jpeg`
* `image/png`
* `image/webp`
* `application/vnd.openxmlformats-officedocument.wordprocessingml.document`
* `application/vnd.openxmlformats-officedocument.spreadsheetml.sheet`

The upload URL response includes:

| Field       | Description                                               |
| ----------- | --------------------------------------------------------- |
| `uploadUrl` | Presigned URL for the direct file upload.                 |
| `objectKey` | Storage object key to send in the confirmation request.   |
| `expiresAt` | Expiry timestamp for the presigned URL.                   |
| `method`    | Upload method. Token document uploads use `PUT`.          |
| `headers`   | Headers that must be sent with the direct storage upload. |

## Upload the file bytes [#upload-the-file-bytes]

Upload the file directly to the returned URL. Use the returned method and
headers. Some storage backends include provider-specific headers in the upload
URL response.

```ts
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 document [#confirm-the-uploaded-document]

After the file upload succeeds, confirm the upload with the returned `objectKey`.
The confirmation creates the token document record and returns the stored document
metadata.

```ts
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 confirmation request repeats the file metadata and adds `objectKey`. It also
accepts `replaceGroupId` when the new upload replaces an earlier document in the
same version group.

The response includes the document `id`, `groupId`, `versionNumber`, `isLatest`,
`fileHash`, `uploadedAt`, and uploader metadata.

DALP calculates `fileHash` from the uploaded file bytes when the upload is
confirmed. Store that value in downstream systems when you need to reconcile that
an asset document record still points to the expected file.

## Record document integrity claims [#record-document-integrity-claims]

Token document upload records and asset-level claims serve different purposes:

* The token document upload flow stores the file metadata, version group, and
  `fileHash` that belongs to the uploaded document record.
* An asset-level document-hash claim can reference a SHA-256 hash, document type,
  and file name without placing the document contents on-chain.

Use the upload flow to manage access, download links, and document versions. Use
an asset-level document-hash claim when an asset also needs an on-chain integrity
reference for a specific document.

## Choose document type and visibility [#choose-document-type-and-visibility]

Choose the document type from the token's asset profile, not from the full token
document catalog. DALP uses the asset type to narrow the choices shown in the
dapp upload dialog, so a stablecoin exposes reserve evidence choices while a
precious metal exposes assay, storage, custody-chain, and insurance choices.

| 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 evidence,
and use `assay_certificate`, `storage_receipt`, or `chain_of_custody` for
precious metal evidence. Use `other` only when the document does not fit a more
specific type.

Visibility controls who can access the document:

* `public`: visible to anyone who can access the token document surface.
* `holders`: visible to token holders.
* `restricted`: limited to explicitly allowed access paths.

Choose the narrowest visibility that fits the operating process and regulatory
basis for the document.

## List documents [#list-documents]

Use the list endpoint to reconcile published documents or populate an asset
document table.

```bash
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 pagination, sorting, filtering, and facets. Sortable
fields include `fileName`, `documentType`, `visibility`, `fileSize`, and
`uploadedAt`. Filterable fields are `fileName`, `documentType`, `visibility`,
`mimeType`, `isLatest`, `fileSize`, and `uploadedAt`.

Filter with the same collection filter shape used by the current list endpoints.
For example, request the latest public prospectuses uploaded after a cutoff time:

```bash
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 only returns the latest non-deleted document records visible to the
caller. Public documents are visible through the token document surface. Holder
visibility requires a caller with an asset role, and restricted visibility
requires governance or admin rights.

The list response uses the standard collection shape with `data`, `meta`, and
`links`. Each document row includes the document identifiers, token address, type,
visibility, group version metadata, file metadata, optional title and description,
`fileHash`, `uploadedAt`, and uploader metadata.

## Download or delete a document [#download-or-delete-a-document]

Request a secure download URL when a user needs to retrieve the file:

```ts
const download = await client.token.documents.getDownloadUrl({
  params: {
    tokenAddress: "0xTOKEN",
    documentId: document.data.id,
  },
});
```

Delete a document only when it should no longer be available from the asset's
document record:

```ts
await client.token.documents.delete({
  params: {
    tokenAddress: "0xTOKEN",
    documentId: document.data.id,
  },
});
```

Keep the business reason and approval evidence for deletes in your operating
records. The delete call removes the document record from the API surface; it is
not a substitute for your regulated record-retention process.

## CLI commands [#cli-commands]

The DALP CLI exposes the same token document lifecycle:

```bash
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_123
```

Use the API or SDK for the direct file upload step because the CLI returns the
presigned URL and object key; it does not upload the local file bytes for you.

## See also [#see-also]

* [KYC document uploads](/docs/developers/developer-guides/api-integration/kyc-document-uploads)
* [Token lifecycle](/docs/developers/developer-guides/api-integration/token-lifecycle)
* [Token holders and transfers](/docs/developers/developer-guides/api-integration/token-holders-transfers)
