Issue & Transfer
to issue or issue+transfer SRR
POST <base_url>/port/api/v1/commerce/srrs
Please replace <base_url> as explained here.
Looking for the flow, a payload to copy, or decoding an error?
Issuance Flow (End-to-End) — flowcharts for single/bulk issuance with new or reused files.
Request Payloads & Recipes — ready-to-use bodies for every scenario (issue to self, issue-on-buyer, custom collection, NFC tags, attachments, bulk).
Errors & Troubleshooting — every status code and message this endpoint can return, with causes and fixes.
Precaution
Multiple issuance
Feel free to include more than one issuance in a single request via the payload array. The minimum number of issue requests is one. Though there is no fixed upper limit for the number of issue requests you can submit at one time, we recommend limiting batches to no more than 250 issue requests for optimal processing.
issue to issuer
File URLs — every file must come from the signed-URL flow
All file URLs in an issuance request must be the finalUrl returned by /commerce/signedUrls. The finalUrl is available immediately in the signed-URL response — no extra processing needed; our system converts it to a permanent IPFS URL at issuance. (URLs already on IPFS — ipfs://… or https://cdn.startrail.io/ipfs/… — are also accepted, but deriving them yourself requires polling /commerce/fileMetadata for the CID and building the URL manually, so prefer the finalUrl.) This applies to all four URL locations:
payload[*].metadata.thumbnailURLpayload[*].metadata.imagepayload[*].metadata.contractTerms.fileURLpayload[*].attachmentFiles[*].url
Why: during issuance, Startrail converts Startbahn-storage URLs to permanent ipfs:// URLs and publishes the files to IPFS. A URL on any other host cannot be published to IPFS — the certificate would forever reference a mutable third-party file with no permanence or integrity guarantee. External URLs are therefore rejected with 400 (see Errors).
The full flow for every file (thumbnail, image, contract terms, attachments):
Request a signed URL (
action: "write") — keep the returnedfinalUrlUpload the file with HTTP
PUTto the signed URLPoll
/commerce/fileMetadatauntil the file'shashandcidare returnedUse the
finalUrlinmetadata.thumbnailURL/image/contractTerms.fileURLorattachmentFiles[*].url
Note: metadata.external_url is exempt — it is an external reference link by design and is never uploaded to IPFS.
Metadata files must also be publicly accessible. thumbnailURL, image and contractTerms.fileURL are served to certificate viewers, so they must be uploaded with a public file category in the signed-URL request: non_attachment_file (recommended for these fields) or artwork. Files uploaded with a private category (certificate, for_authenticity, installation) land in the private bucket, cannot be served or published to IPFS, and are rejected with 400 when referenced by these metadata fields. attachmentFiles[*].url may use any category, including private ones — attachments are folded into the metadata as content hashes, not URLs.
Headers
commerce-api-key*
string
Commerce API Key
issuer-address*
string
Contract Address of API Key owner
Request Body
requestId
string
A requestId given by the caller, to ensure requests are only processed once. If the requestId is known and processed before, api will not process this call again, and respond with an error. A good practice is using random UUID.
*requestId must be unique for a given issuer-address. As a result any duplicate combination of requestId and issuer-address is instantly rejected with no impact on either.
payload[*].externalId*
string
An ID to identify the record in your system. We recommend to use UUID, but it can use any string as long it is unique in your system.
payload[*].metadata*
object
The metadata to be issued as a complex object. Detailed schema specification can be found here.
The API accepts versions 2.0 and higher.
payload[*].artistAddress*
string
The ethereum address of the artist of the artwork.
payload[*].isPrimaryIssuer*
boolean
If you are the primary issuer of this NFT, set this to true.
payload[*].lockExternalTransfer*
boolean
If you want to prevent your NFTs to be transferred on decentralized marketplaces, set this to true.
payload[*].to
string
Ethereum address target the NFT should be sent to after minting (Issue on Buyer).
If none is given the NFT will be minted into your LUW by default.
payload[*].attachmentFiles
Array<object>
Attachment files that will be included in SRR.
payload[*].attachmentFiles[*].name
string
The name of file.
This is used for when the file is downloaded or shown. The extension is recommended to be the same as the actual uploaded file.
payload[*].attachmentFiles[*].category
string
Please refer to this page to understand the difference among the categories.
Please note that contract terms and thumbnail are NOT attachment files. The URL for contract terms and thumbnail are needed for metadata. (See metadata attribute)
payload[*].attachmentFiles[*].url
string
The URL must be under Startbahn's GCS bucket. Use the finalUrl field returned by the signed URL endpoint, or a URL that Startbahn has pre-provisioned for you. Any other URL is rejected without issuing the SRR.
For each attachmentFiles entry, the API resolves the URL to its GCS path, reads the precomputed SHA-256 from the storage object metadata, and folds {category, hash} entries into metadata.digitalComponents (artwork category) or metadata.attachmentFiles (other categories) before issuance. You do not need to populate those metadata sub-fields yourself.
payload[*].collectionAddress
string
The address of collection that the SRR will belong to. This collection must be owned by the caller issuer-address.
payload[*].externalUrls
Array<string>
Deprecated. Use the external_url field in metadata.
To remain backward compatible the external URLs is still allowed and is simply passing the first string in the array to the metadata as external_url). This mapping is redundant and will be removed.
The API responds with 201. see Response Body results[*].status for details of each entry.
results
results of the request
Array
results[*].srr
Minimal identifying details of the issued SRR. Richer information (issuer, artist, collection details, on-chain metadata, history, ownership) must be queried from the subgraph once the transaction is mined.
object
results[*].srr.tokenId
NFT token ID of the SRR.
string
results[*].srr.metadataCID
IPFS CID of the SRR metadata. Resolve via the Startrail IPFS CDN gateway: https://cdn.startrail.io/ipfs/<cid>. See IPFS CDN gateway.
string
results[*].srr.metadataURL
Convenience HTTPS URL that resolves the SRR metadata via the Startrail IPFS CDN gateway. Same content as metadataCID resolved through https://cdn.startrail.io/ipfs/.
string
results[*].srr.collectionContractAddress
Collection contract address the SRR belongs to. null if the SRR was not issued under a custom collection.
string | null
results[*].externalId
ID to identify the SRR. Defined by the client when calling.
string
results[*].status
waiting_for_mining – wait for completion of blockchain mining. The status (and any further SRR data) can then be confirmed via the subgraph or less favorably via the Get SRR by Token Id REST endpoint.
string
The API responds with 400 if request body invalid or some validation errors that can be instantly detected.
Multiple issuance
In case of multiple/bulk issuance as soon as one case failed, the rest of the payload will be definitely failed as well.
Example of duplicated metadata
Example of DTO validation error
The API responds with 5xx if there are other issues, such as deeper validation errors.
Swagger Endpoint (Test Environment)
Required Permissions
Check the parent page.
Request Body Example
Code Example
Check parent page.
NFC / IC tags — if you have a TAG for a physical artwork, add both chipUIDs and startbahnCertICTagUIDs at the same time, containing the same values. The value is an array of the Chip UIDs. For example:
In schema v2.2 the two fields are mutually required — supplying only one fails metadata validation. chipUIDs is the current field; startbahnCertICTagUIDs is the deprecated alias kept in sync. Each UID consumes one unit of the LUW's NFC-tag allowance — exceeding it returns LUW missing NFC tag!. For a copy-paste example see NFC tags recipe.
Last updated
Was this helpful?