By using the API/SDK in this document in any way, you agree to . Please read these Terms carefully before using the API/SDK.
UML Diagram
Endpoints Used
Please note that we may add a new field. So make sure that your implementation can support accepting new fields without breaking your implementation. In case we remove the field, we will deprecate it first and let you know beforehand.
Required Permissions
You need to have a Licensed User
See issuer-address in the following Required Headers section.
You need to have an API Key
See commerce-api-key in the following Required Headers section.
Required Headers
header
value
issuer-address
The Ethereum address of your LUW
commerce-api-key
The API key you generated in the web application
Content-Type
application/json
accept
application/json
Complete Code Example
In this example, we will do
Get a signed URL to upload 1 file
Upload 1 file using a signed URL
Check the file readiness
To simplify the example, we use the same uploaded file's final URL for
contract terms
thumbnail
attachment file
Note:
This example does not cover the webhook on the client’s side that Startbahn API will call when issuance or mining failure happens.
save it as example.ts
npx ts-node ./script-address/example.ts
import fs from 'fs'
import fetch from 'node-fetch'
import { v4 as uuidv4 } from 'uuid'
main('issuerAddress', 'artistAddress', 'apiKey', 'receiverAddress') // Use your staging credential to test
async function main(
luwIssuer: string,
luwArtist: string,
apiKey: string,
toAddress: string | undefined
) {
console.log('Starting the issue API example')
const headersCommerceApi = {
'Content-Type': 'application/json',
accept: 'application/json',
'commerce-api-key': apiKey,
'issuer-address': luwIssuer,
}
/////////////////////////
// GENERATE SIGNED URL //
// STEP 7 - 8 //
/////////////////////////
console.log('Starting to generate signed URL')
const signedUrlEndpoint =
'https://api-stg.startrail.startbahn.jp/port/api/v1/commerce/signedUrls'
const fileName = `test-${uuidv4()}.txt`
const fileWs = fs.createWriteStream(fileName)
fileWs.write('test')
fileWs.end()
const requestBodySignedUrl = {
payload: [
{
filename: fileName,
category: 'artwork',
},
],
action: 'write',
}
const responseSignedUrl = await fetch(signedUrlEndpoint, {
method: 'POST',
body: JSON.stringify(requestBodySignedUrl),
headers: headersCommerceApi,
})
if (!responseSignedUrl.ok) {
console.log('handle the error')
}
const jsonSignedUrlResponse = await responseSignedUrl.json()
const contentType = jsonSignedUrlResponse.results[0].contentType
// Upload the file to the Signed URL
// example using cURL
// `curl -X PUT -H 'Content-Type: text/plain' --upload-file my-file.txt '${res.url}
const signedUrl = jsonSignedUrlResponse.results[0].url
// save res.finalUrl and use it for Issue endpoint
const finalUrl = jsonSignedUrlResponse.results[0].finalUrl
/////////////////////////////
// UPLOAD USING SIGNED URL //
// STEP 9 //
/////////////////////////////
console.log('Starting to upload the file')
const fileRs = fs.createReadStream(fileName)
const responseUpload = await fetch(signedUrl, {
method: 'PUT',
headers: {
'Content-Type': contentType,
},
body: fileRs,
})
if (!responseUpload.ok) {
console.log('handle the error')
}
///////////////////////////
// CHECK FILE READINESS //
// STEP 11 - 12 //
///////////////////////////
console.log('Starting to check the file readiness')
await sleep(360000) // wait to make sure the hash is calculated
const fileInfoEndpoint =
'https://api-stg.startrail.startbahn.jp/port/api/v1/commerce/fileMetadata'
const requestBodyFileInfo = {
payload: [
{
filename: fileName,
category: 'artwork',
},
],
}
const responseFileInfo = await fetch(fileInfoEndpoint, {
method: 'POST',
body: JSON.stringify(requestBodyFileInfo),
headers: headersCommerceApi,
})
if (!responseFileInfo.ok) {
console.log('handle the error')
}
const jsonFileInfoResponse = await responseFileInfo.json()
const calculatedHash = jsonFileInfoResponse.results[0].hash
if (!calculatedHash) {
console.log('There is error in hash calculation, contact Startbahn')
}
//////////////////
// ISSUE SRR //
// STEP 13 - 16 //
//////////////////
console.log('Starting to issuen an SRR')
const issueEndpoint =
'https://api-stg.startrail.startbahn.jp/port/api/v1/commerce/srrs'
const singlePayload: any = {
externalId: uuidv4(),
artistAddress: luwArtist,
isPrimaryIssuer: true,
lockExternalTransfer: false,
// If the uploaded file is thumbnail or contract terms, it should be put on the metadata
metadata: randomizeMetadata(finalUrl, finalUrl),
attachmentFiles: [
{
name: requestBodyFileInfo.payload[0].filename,
// If the uploaded file is an attachment file, put it in this field
url: finalUrl,
category: requestBodyFileInfo.payload[0].category,
},
],
}
if (toAddress) {
singlePayload.to = toAddress
}
const requestBodyissue = {
payload: [singlePayload],
}
const responseIssue = await fetch(issueEndpoint, {
method: 'POST',
body: JSON.stringify(requestBodyissue),
headers: headersCommerceApi,
})
if (!responseIssue.ok) {
console.log('handle the error')
}
const jsonIssueResponse = await responseIssue.json()
console.log(jsonIssueResponse)
}
async function sleep(milliseconds: number): Promise<NodeJS.Timeout> {
return new Promise((resolve) => setTimeout(resolve, milliseconds))
}
function randomizeMetadata(
thumbnailURL: string,
contractTermsFileURL: string
) {
return {
$schema:
'https://api.startrail.io/api/v1/schema/registry-record-metadata.v2.1.schema.json',
$schemaIntegrity:
'sha256-15f8e99eb9d4292287282942db2f2de9bbcc4761c555c6f7da23feec010c1221',
title: {
en: 'A title-' + uuidv4(),
ja: 'タイトル-' + uuidv4(),
zh: '一个标题-' + uuidv4(),
},
size: {
width: 200.0,
height: 400.0,
depth: 12.4,
unit: 'pixel',
flexibleDescription: {
en: 'flexibleDescription comes here',
ja: '自由だーーー',
},
},
medium: {
en: 'Oil on canvas',
ja: 'キャンバスに油彩',
zh: '布面油画',
},
edition: {
uniqueness: 'unique work',
proofType: 'ED',
number: 1,
totalNumber: 3,
note: {
en: 'some extra notes in 1 or more languages',
},
},
contractTerms: {
royaltyRate: 15.7,
fileURL: contractTermsFileURL,
},
note: {
en: 'note',
zh: '注意',
},
thumbnailURL,
yearOfCreation: {
en: 'around 2010-2020',
ja: '2010年から2020年頃',
},
isDigital: true,
name: 'some nft name',
description: 'some nft description',
image:
'https://storage.googleapis.com/opensea-prod.appspot.com/puffs/3.png',
external_url: 'https://startrail.io/',
}
}
Steps 7 - 8:
Step 9: Upload file to Google Cloud Storage, use Signed URL as the endpoint,