# Startrail PORT: All in one document for API/SDK

Developer Guide

### What is Startrail API?

Connect to our Issue API, Transfer API, and Wallet Integration SDK and build your own web3 website, app, or platform

### Why Startrail API?

Bring your NFT app, ecommerce, or marketplace to life faster and at lower cost with our Issue & Transfer API, Secondary Transfer API, and Wallet Integration SDK - nocrypto currency integration required.

### Agreement

In order to refer or use API/SDK, you should agree to terms and conditions. Please read [Terms of Service](https://help.port.startrail.io/hc/en-us/articles/12968031055511) of Startrail PORT carefully before getting started. If you have any questions, please [Contact Us](https://startbahn.io/contact).

For each product, please check the respective section in the side bar.

### Using an AI coding assistant?

This documentation ships machine-readable resources so tools like Claude, Cursor and Copilot can integrate Startrail accurately:

* **`llms.txt`** — a curated index of every page in these docs ([llmstxt.org](https://llmstxt.org) format), with a one-line description and link for each. Point your AI tool at it for a fast, accurate map of the API and SDK.
* **`AGENTS.md`** — orientation for AI agents: the two integration surfaces (REST API vs. browser SDK), the domain glossary (SRR, LUW, EOA, collection), environment base URLs, and the canonical SDK flow.

The browser SDK additionally bundles its own reference at `node_modules/@startbahn/startrail-sdk-js/llms.txt` — prefer it when generating SDK code.


# URL per environment

Base urls for endpoints

{% hint style="warning" %}
In our Test environment, we’ve moved from the old Polygon Mumbai test network to the new Polygon Amoy test network on April 10, 2024. Because of this change, any data like SRRs, LUWs, etc., that you had created before are disappeared. We suggest recreating any necessary data for testing. For more information, please reach out to Startbahn from [here](https://startbahn.io/contact).
{% endhint %}

<table><thead><tr><th width="202">Environment</th><th width="541.3333333333333">URL</th></tr></thead><tbody><tr><td>Production</td><td>https://api.startrail.io</td></tr><tr><td>Test</td><td>https://api-stg.startrail.startbahn.jp</td></tr></tbody></table>


# Issue & Transfer SRR (NFT)

If you have any concerns, please [contact us](https://startbahn.io/contact).

By using the API/SDK in this document in any way, you agree to [Startrail PORT API/SDK Terms of Service](https://help.port.startrail.io/hc/en-us/articles/12968031055511). Please read these Terms carefully before using the API/SDK.

## UML Diagram

<figure><img src="/files/iVIH2dDuYN1IJMfVtv2K" alt=""><figcaption></figcaption></figure>

## Endpoints Used

{% hint style="info" %}
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.
{% endhint %}

* Steps 7 - 8: [Request Signed URL for Upload/Download Files](/issue-transfer-api/issue-and-transfer-srr-nft/request-signed-url-for-upload-download-files)
* Step 9: Upload file to Google Cloud Storage, use Signed URL as the endpoint, [Google Documentation](https://cloud.google.com/storage/docs/access-control/signed-urls)
* Steps 11 - 12: [File Information Metadata](/issue-transfer-api/issue-and-transfer-srr-nft/file-information-metadata)
* Steps 13 - 16: [Issue & Transfer SRR](/issue-transfer-api/issue-and-transfer-srr-nft/issue-and-transfer)
* Step 17: [Webhook Setup](/issue-transfer-api/issue-and-transfer-srr-nft/webhook-setup)

## Reference

{% content-ref url="/pages/s1m5P6ea2FRAgxjhC1cL" %}
[Issuance Flow (End-to-End)](/issue-transfer-api/issue-and-transfer-srr-nft/issuance-flow)
{% endcontent-ref %}

{% content-ref url="/pages/0jhQ5HkfFA9BLusH5SmK" %}
[Request Payloads & Recipes](/issue-transfer-api/issue-and-transfer-srr-nft/payloads)
{% endcontent-ref %}

{% content-ref url="/pages/oOU0grtYrgSIzFZqyMWb" %}
[Errors & Troubleshooting](/issue-transfer-api/issue-and-transfer-srr-nft/errors)
{% endcontent-ref %}

## 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

1. Get a signed URL to upload 1 file
2. Upload 1 file using a signed URL
3. Check the file readiness
4. To simplify the example, we use the same uploaded file's final URL for
   * contract terms
   * thumbnail
   * image
   * 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.
* You may need [TS-Node](https://www.npmjs.com/package/ts-node) and [NPX](https://www.npmjs.com/package/npx) to run it

  * save it as `example.ts`

  ```jsx
  npx ts-node ./script-address/example.ts
  ```

```typescript
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, image 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 must also be an uploaded file's finalUrl (treated like thumbnailURL)
    image: thumbnailURL,
    external_url: 'https://startrail.io/',
  }
}
```


# Issuance Flow (End-to-End)

End-to-end recommended flow for issuing SRRs — single and bulk, with new files or reusing previously uploaded files. Flowcharts + step tables.

This page shows the **recommended order of API calls** to issue SRRs, from file upload to mining confirmation. For payload bodies see [Request Payloads & Recipes](/issue-transfer-api/issue-and-transfer-srr-nft/payloads); for failures see [Errors & Troubleshooting](/issue-transfer-api/issue-and-transfer-srr-nft/errors).

{% hint style="info" %}
**The one rule that shapes the whole flow:** every file an SRR references (`thumbnailURL`, `image`, `contractTerms.fileURL`, `attachmentFiles[*].url`) must be uploaded via [`/commerce/signedUrls`](/issue-transfer-api/issue-and-transfer-srr-nft/request-signed-url-for-upload-download-files) and referenced by the returned **`finalUrl`** — see [File URLs](/issue-transfer-api/issue-and-transfer-srr-nft/issue-and-transfer#file-urls-every-file-must-come-from-the-signed-url-flow). The `finalUrl` is in the signed-URL response **immediately**, so you can build your issuance payload right away; you only **wait** (poll [`/fileMetadata`](/issue-transfer-api/issue-and-transfer-srr-nft/file-information-metadata)) for the file's `hash` + `cid` before actually calling the issue endpoint.
{% endhint %}

## The big picture

```mermaid
flowchart TD
    A([Start: issue one or more SRRs]) --> B{"New files, or reusing uploaded ones?"}
    B -- "New files" --> C["1 POST /commerce/signedUrls<br/>action: write - one entry per file<br/>(keep each finalUrl)"]
    C --> D["2 HTTP PUT each file<br/>to its signed url"]
    D --> E["3 POST /commerce/fileMetadata<br/>poll until hash + cid present<br/>for every file"]
    B -- "Reusing files" --> F["Use the stored finalUrl<br/>(files already have hash + cid)"]
    E --> G["4 POST /commerce/srrs<br/>requestId + payload (1..n entries)<br/>all file URLs = finalUrl"]
    F --> G
    G --> H["5 Response 201<br/>status: waiting_for_mining<br/>(tokenId + metadataCID per entry)"]
    H --> I["6 Webhook fires on<br/>mining success / failure"]
    I --> J([Read full SRR data<br/>from the subgraph])
```

The same six steps apply to **single and bulk** — bulk just carries more entries in the same two arrays (`signedUrls.payload[]` for files, `srrs.payload[]` for SRRs).

## Flow A — single SRR with a new file

```mermaid
sequenceDiagram
    autonumber
    participant C as Your server
    participant API as Commerce API
    participant GCS as Storage
    C->>API: POST /commerce/signedUrls {payload:[{filename, category}], action:"write"}
    API-->>C: results[0]: { url, finalUrl, contentType }
    C->>GCS: PUT file to url (Content-Type from response)
    loop until hash + cid present
        C->>API: POST /commerce/fileMetadata {payload:[{filename, category}]}
        API-->>C: { hash, cid } or STILL_IN_CALCULATION
    end
    C->>API: POST /commerce/srrs { requestId, payload:[ SRR with finalUrl ] }
    API-->>C: 201 { results:[{ status: waiting_for_mining, srr:{ tokenId, metadataCID } }] }
    API--)C: Webhook: issuance mined (or failed)
```

| # | Step               | Endpoint                                                                                                 | You provide                                                                                                                                                                                                        | You get / wait for                                                  |
| - | ------------------ | -------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------- |
| 1 | Request signed URL | `POST /commerce/signedUrls`                                                                              | `filename` (unique per issuer, no spaces, with extension) + `category` (see the [category table](/issue-transfer-api/issue-and-transfer-srr-nft/request-signed-url-for-upload-download-files)) + `action: "write"` | `url` (upload target), **`finalUrl`** (store it!), `contentType`    |
| 2 | Upload             | HTTP `PUT` to `url`                                                                                      | the file bytes, `Content-Type` from step 1                                                                                                                                                                         | `200`                                                               |
| 3 | Wait for readiness | `POST /commerce/fileMetadata`                                                                            | same `filename` + `category`                                                                                                                                                                                       | poll until **`hash` and `cid`** are present (usually a few minutes) |
| 4 | Issue              | `POST /commerce/srrs`                                                                                    | `requestId` (fresh UUID) + `payload[0]` with `metadata.thumbnailURL` / `image` (= `finalUrl`), optional `attachmentFiles`                                                                                          | `201` with `tokenId`, `metadataCID`, `status: waiting_for_mining`   |
| 5 | Confirm            | [Webhook](/issue-transfer-api/issue-and-transfer-srr-nft/webhook-setup) / [subgraph](/subgraph/subgraph) | —                                                                                                                                                                                                                  | mining outcome; full SRR data                                       |

{% hint style="warning" %}
Use the **public `non_attachment_file` category** for files going into `thumbnailURL` / `image` / `contractTerms.fileURL` — private categories are rejected for those fields. One file can serve several roles (e.g. the same `finalUrl` as `thumbnailURL` **and** `image`).
{% endhint %}

## Flow B — bulk issuance (many SRRs, many files)

Bulk is the **same flow with batched arrays** — do **not** loop the single flow per SRR.

```mermaid
flowchart LR
    subgraph "Files (one batch)"
        A["POST /signedUrls<br/>payload: N file entries"] --> B["PUT × N<br/>(parallel OK)"]
        B --> C["POST /fileMetadata<br/>payload: N entries<br/>poll until ALL ready"]
    end
    subgraph "Issuance (one request)"
        D["POST /srrs<br/>requestId + payload: M SRR entries<br/>(each with its finalUrls)"] --> E["201: M results<br/>waiting_for_mining"]
    end
    C --> D
```

Key differences from single:

| Concern                                      | Rule                                                                                                                                                                     |
| -------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Files per `signedUrls` / `fileMetadata` call | Any number (`payload[]` array) — batch them, upload in parallel                                                                                                          |
| SRRs per `srrs` call                         | 1..n in `payload[]`; **≤ 250 recommended** (no hard cap)                                                                                                                 |
| Atomicity                                    | **All-or-nothing**: one failed entry cancels the whole batch — fix the entry named in `results[]` and resubmit everything                                                |
| Readiness                                    | Wait until **every** referenced file has `hash` + `cid` before issuing — one not-ready file fails the whole batch                                                        |
| NFC tags                                     | The chip **sum across all entries** must fit the LUW's NFC allowance — see [NFC errors](/issue-transfer-api/issue-and-transfer-srr-nft/errors#nfc-tags-and-quota-errors) |
| Idempotency                                  | One `requestId` for the whole batch; reuse is rejected (`Duplicate Request Error`)                                                                                       |

## Flow C — reusing previously uploaded files

Files stay in storage and keep their `finalUrl`, `hash` and `cid` forever — **upload once, reference many times**.

```mermaid
flowchart TD
    A{"Do you still have the finalUrl?"} -- Yes --> D["POST /commerce/srrs<br/>reference the stored finalUrl directly<br/>(no upload, no polling needed)"]
    A -- No --> B["POST /commerce/signedUrls<br/>same filename + category, action: read<br/>(returns the finalUrl again - no upload)"]
    B --> C["Optional: POST /commerce/fileMetadata<br/>to confirm the file still exists and is ready"]
    C --> D
```

* **Store `finalUrl`s** alongside your own records at upload time — that makes reuse a zero-step operation.
* A reused file needs **no re-upload and no polling**: its `hash`/`cid` were computed after the original upload.
* Reuse is natural when: many editions share one artwork image, a shared contract-terms PDF, or re-issuing after a failed batch.
* Filenames are **unique per `issuer-address`** — you cannot re-upload a new file under a used name; pick a new name for new content. Identical content under a different name resolves to the **same CID** on IPFS, so duplicates are harmless.

## Checklist (for humans and LLMs)

```
RECOMMENDED ISSUANCE FLOW (commerce API, base: <base_url>/port/api/v1)
Headers on every call: commerce-api-key, issuer-address, Content-Type: application/json

FOR EACH NEW FILE (batchable):
  1. POST /commerce/signedUrls   body: {payload:[{filename,category}], action:"write"}
     -> save results[i].finalUrl               (available immediately)
     -> category: non_attachment_file for thumbnail/image/contract-terms files,
        artwork for digital-artwork files, private categories for confidential attachments
  2. PUT <results[i].url>        body: file bytes, Content-Type: results[i].contentType
  3. POST /commerce/fileMetadata body: {payload:[{filename,category}]}
     -> repeat until every file returns hash AND cid   (do not issue before this)

FOR REUSED FILES: skip 1-3, use the stored finalUrl
  (lost it? POST /commerce/signedUrls with action:"read" returns it again)

ISSUE (single or bulk, same endpoint):
  4. POST /commerce/srrs body: {requestId:<fresh UUID>, payload:[1..n SRR entries]}
     -> every thumbnailURL / image / contractTerms.fileURL / attachmentFiles[].url
        MUST be a finalUrl; external URLs are rejected (400)
     -> batch is atomic; <=250 entries recommended
     -> response 201: results[i].srr.tokenId + metadataCID, status waiting_for_mining

CONFIRM:
  5. Webhook notifies mining success/failure; read full SRR data from the subgraph.
```

## Common pitfalls

| Pitfall                                      | What happens                                                               | Avoid by                                                                                                                            |
| -------------------------------------------- | -------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- |
| Issuing before `cid` is ready                | `400` `… not ready for IPFS publication …` / `Attachment hash not ready …` | Step 3 — poll `/fileMetadata` first                                                                                                 |
| External file URL (your own CDN, Shopify, …) | `400` `File URLs must be Startbahn storage URLs …`                         | Always use the `finalUrl`                                                                                                           |
| Private category for a thumbnail/image       | `400` `… must be publicly accessible …`                                    | Upload with `non_attachment_file`                                                                                                   |
| Reusing a `requestId`                        | `400` `Duplicate Request Error`                                            | Fresh UUID per request; reuse only intended as a retry-guard                                                                        |
| Retrying a failed bulk with the same body    | `400` `Request Content Error: … already logged`                            | Fix the failing entry, keep a new `requestId`                                                                                       |
| Assuming `201` = minted                      | SRR may still fail at mining                                               | Wait for the [webhook](/issue-transfer-api/issue-and-transfer-srr-nft/webhook-setup); verify via the [subgraph](/subgraph/subgraph) |

{% content-ref url="/pages/0jhQ5HkfFA9BLusH5SmK" %}
[Request Payloads & Recipes](/issue-transfer-api/issue-and-transfer-srr-nft/payloads)
{% endcontent-ref %}

{% content-ref url="/pages/oOU0grtYrgSIzFZqyMWb" %}
[Errors & Troubleshooting](/issue-transfer-api/issue-and-transfer-srr-nft/errors)
{% endcontent-ref %}


# Request Signed URL for Upload/Download Files

to generate signed URLs both for uploads and downloads

<mark style="color:green;">`POST`</mark> `<base_url>/port/api/v1/commerce/signedUrls`

Please replace `<base_url>` as explained [here](/readme/url-per-environment).

{% hint style="success" %}
**This endpoint is the front door for every file your SRRs reference.** Thumbnails, images, contract-terms PDFs and attachment files must all be uploaded through the signed URL returned here, and referenced in the issuance request by the returned **`finalUrl`** — that is what allows Startrail to compute the file's IPFS CID and publish it permanently to IPFS at issuance. Files hosted anywhere else are [rejected by the Issue & Transfer endpoint](/issue-transfer-api/issue-and-transfer-srr-nft/issue-and-transfer#file-urls-every-file-must-come-from-the-signed-url-flow).

Flow: **signed URL (`write`) → `PUT` upload → poll** [**`/fileMetadata`**](/issue-transfer-api/issue-and-transfer-srr-nft/file-information-metadata) **until `hash` + `cid` appear → issue using `finalUrl`**.

**Pick the right `category`.** It matters in two independent ways — **logically** (which storage bucket the file lands in, hence who can access it) and **semantically** (what the file *means* in the certificate when referenced from `attachmentFiles[]`):

Rule of thumb: **`non_attachment_file` for anything referenced by URL in the metadata** (thumbnail, image, contract terms); **`artwork` only for files that&#x20;*****are*****&#x20;the (digital) work**; private categories for confidential attachments. See also [What is an Artwork File](https://help.port.startrail.io/hc/en-us/articles/7720691967767-What-is-an-Artwork-File-).
{% endhint %}

| Category              | Logical: bucket | Semantic: meaning in the SRR                                                                                                                   | In `attachmentFiles[*].category`    | In `thumbnailURL` / `image` / `contractTerms.fileURL`                     |
| --------------------- | --------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------- | ------------------------------------------------------------------------- |
| `non_attachment_file` | public          | Supporting file that is *not* part of the work — referenced by URL only                                                                        | ❌ not accepted                      | ✅ **recommended**                                                         |
| `artwork`             | public          | A digital component **of the artwork itself** — its content hash is written into `metadata.digitalComponents` (part of the authenticity claim) | ✅ → folded into `digitalComponents` | ✅ accepted (public), but semantically reserve it for actual artwork files |
| `certificate`         | **private**     | Certificate document attached to the work                                                                                                      | ✅ → folded into `attachmentFiles`   | ❌ rejected — private bucket, viewers cannot access                        |
| `for_authenticity`    | **private**     | Authenticity evidence attached to the work                                                                                                     | ✅ → folded into `attachmentFiles`   | ❌ rejected                                                                |
| `installation`        | **private**     | Installation instructions/records attached to the work                                                                                         | ✅ → folded into `attachmentFiles`   | ❌ rejected                                                                |

### Precaution

#### File Size

{% hint style="danger" %}
Our system currently only support file sizes with a maximum limit of strictly less than 2GB, or in other words:

```
    filesize < 2^31 
```

This essentially implies that any user interaction with our API involving data transfer would necessitate adherence to this requirement. Manifestly, if a file size exceeds the defined threshold of 2GB, it may possibly impede the operation of the application.
{% endhint %}

#### File Name

{% hint style="danger" %}
File name

1. should not contain space.
2. should contain the file extension, including but not limited to `.jpg`, `.png`, `.pdf`, etc.
3. use a unique name for each file. Filename is unique per`issuer-address` otherwise it will fail.

   This is to avoid modification of files that are already used in SRR.\
   However given the upgrade to the IPFS protocol, there is no need to be concerned for the filename in terms of its look. The filename is not going to exist in the minted SRR since it is going to be converted to IPFS and the filename will be replaced with `cid` that is a function of the file's content and not file's name.

\*In order to make the file names unique anyway, you can prefix/suffix the name with a unique number such as `Date.now()` .

\*\*Given the underlying usage of IPFS, there is no need to be worried about duplicated contents. Since if two files with different name but same content will eventually be replaced with the same `ipfs` url.
{% endhint %}

#### Headers

| Name                                               | Type   | Description                        |
| -------------------------------------------------- | ------ | ---------------------------------- |
| commerce-api-key<mark style="color:red;">\*</mark> | string | Commerce API Key.                  |
| issuer-address<mark style="color:red;">\*</mark>   | string | Contract Address of API Key owner. |

#### Request Body

| Name                                                    | Type                    | Description                                                                                                                                                                                                                                                                                                                                                            |
| ------------------------------------------------------- | ----------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| action<mark style="color:red;">\*</mark>                | string (write and read) | <p>Field to specify the action of the Signed URL. Value</p><p><code>write</code></p><p>to request upload signed URL, and</p><p><code>read</code></p><p>to request download signed URL.</p>                                                                                                                                                                             |
| payload<mark style="color:red;">\*</mark>               | array                   | Array of signed URL request.                                                                                                                                                                                                                                                                                                                                           |
| payload\[\*].filename<mark style="color:red;">\*</mark> | string                  | <p>The filename must meet the constraints mentioned <a href="#file-name">above</a>.</p><p>If the client needs to delete an existing file, please contact Startbahn.</p>                                                                                                                                                                                                |
| payload\[\*].category<mark style="color:red;">\*</mark> | string                  | <p>Please refer to the category table at the top of this page for how each category is treated (bucket + meaning), and <a href="https://help.port.startrail.io/hc/en-us/articles/7720691967767-What-is-an-Artwork-File-">this page</a> for product-level background.</p><p>Use <code>non\_attachment\_file</code> to upload thumbnails, images and contract terms.</p> |

{% tabs %}
{% tab title="201: Created " %}
The API responds with 201 when the signed URL is created. The validity of the signed URL is **15 minutes** after it is created.

The client’s back-end needs to consider its upload speed. If the files that need to be uploaded are many, client’s back-end may consider splitting the signed URL request to ensure that all of the files are uploaded.

{% tabs %}
{% tab title="Body" %}

| Body Attribute           | Description                                                                                                                                                                                                                                                                                      | Format |
| ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------ |
| results                  | results of the request                                                                                                                                                                                                                                                                           | Array  |
| results\[\*].filename    | Name of the file, same as one in the request                                                                                                                                                                                                                                                     | string |
| results\[\*].contentType | `Content-Type` that was chosen and set for the file. You need to set the same value in `Content-Type` header when uploading or downloading the file with the signed URL. Otherwise, you will get an error about signature mismatch.                                                              | string |
| results\[\*].url         | The signed URL that client can use to perform action such as upload or download. NOTE: This URL is only valid for 15 minutes and should not be saved. Please check the [reference below](/issue-transfer-api/issue-and-transfer-srr-nft/request-signed-url-for-upload-download-files#reference). | string |
| results\[\*].finalUrl    | The URL that client should save and use when calling issue endpoint. Check below for details                                                                                                                                                                                                     | string |

#### Usage of Final URL

| category                                     | Usage                                                                                                                              | example                                                                                                                               |
| -------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- |
| artwork                                      | On issue endpoint, use it for `payload.attachmentFiles[*].url`                                                                     | <https://static-files-stg.startrail.startbahn.jp/srr-images/0xf1B51E02804A7AF4Eb4c2f57dc9a05510A513C17/221208_royalty-01-001.jpg>     |
| certificate, installation, for\_authenticity | On issue endpoint, use it for `payload.attachmentFiles[*].url`                                                                     | <https://storage.googleapis.com/artwork-staging-images/srr-images/luw-address-1/certificate.tif>                                      |
| non\_attachment\_file                        | use it for the field in metadata on the `image`, `payload[*].metadata.thumbnailURL` or `payload[*].metadata.contractTerms.fileURL` | <https://storage.googleapis.com/artwork-staging-images/srr-images/0xf1B51E02804A7AF4Eb4c2f57dc9a05510A513C17/Contract_Terms_test.pdf> |
| {% endtab %}                                 |                                                                                                                                    |                                                                                                                                       |

{% tab title="Example" %}

```javascript
{
  "results": [
    // Example result for non_attachment_file and artwork category
    {
      "filename": "artwork.jpg",
      "contentType": "image/jpeg",
      "url": "https://storage.googleapis.com/bucket/directory/luw-address/artwork.jpg?signature=xyz",
      "finalUrl": "https://static-files.startrail.io/directory/luw-address/artwork.jpg"
    },
    // Example for other categories
    {
      "filename": "certificate.jpg",
      "contentType": "image/jpeg",
      "url": "https://storage.googleapis.com/bucket/directory/luw-address/certificate.jpg?signature=xyz",
      "finalUrl": "https://storage.googleapis.com/bucket/directory/luw-address/certificate.jpg"
    }
  ]
}
```

{% endtab %}
{% endtabs %}
{% endtab %}

{% tab title="400: Bad Request " %}
The API responds with 400 if the request body is invalid.

```javascript
// If the payload is incorrect
{
  "statusCode": 400,
  "message": [
    "payload.0.category must be one of the following values: certificate,for_authenticity,artwork,installation,non_attachment_file"
  ]
}
```

{% endtab %}

{% tab title="500: Internal Server Error " %}
The API responds with 5xx if there is an issue between the network and also storage provider, currently it is Google Cloud Storage.

```
// If the network is error or unknown error. Startbahn side needs to check.
{
  "statusCode": 502,
  "message": "Bad Gateway"
}
```

{% endtab %}
{% endtabs %}

## Swagger Endpoint (Test Environment)

[Swagger to test](https://api-stg.startrail.startbahn.jp/port/api#/public/CommerceController_bulkGenerateSignedUrls).

## Required Permissions

Check [parent page](/issue-transfer-api/issue-and-transfer-srr-nft).

## Request Body Example

### Signed URL for upload

```json
// Example for thumbnail
{
  "payload": [
    {
      "filename": "thumbnail.jpg",
      "category": "non_attachment_file"
    }
  ],
  "action": "write"
}

// Example for contract terms
{
  "payload": [
    {
      "filename": "contract.pdf",
      "category": "non_attachment_file"
    }
  ],
  "action": "write"
}

// Example for other categories
{
  "payload": [
    {
      "filename": "certificate.pdf",
      "category": "certificate"
    }
  ],
  "action": "write"
}
```

### Signed URL for Download

```json
{
  "payload": [
    {
      "filename": "certificate.pdf",
      "category": "certificate"
    }
  ],
  "action": "read"
}
```

## Code Example

Check [parent page](/issue-transfer-api/issue-and-transfer-srr-nft).

## Reference

<https://cloud.google.com/storage/docs/access-control/signed-urls>


# File Information Metadata

to get file metadata

<mark style="color:green;">`POST`</mark> `<base_url>/port/api/v1/commerce/fileMetadata`

Please replace `<base_url>` as explained [here](/readme/url-per-environment). Please note that file information metadata is different from SRR metadata.

### Precaution

#### Multiple files

{% hint style="info" %}
When requesting for multiple files:

If all files are found in our buckets and the requested is authorized for the access, the response is going to succeed and include the respective information per requested file. However in any other case the response status is not 200 and the return information will be only including inaccessible files that is <= the requested files.
{% endhint %}

#### Use case

{% hint style="info" %}
Use this endpoint to confirm an uploaded file is **ready for issuance**: readiness means the response includes both the **`hash`** (SHA-256) and the **`cid`** (IPFS content ID) for the file. Until then, referencing the file in [Issue & Transfer](/issue-transfer-api/issue-and-transfer-srr-nft/issue-and-transfer) fails with `Attachment hash not ready …` (attachments) or `Metadata file(s) not ready for IPFS publication …` (`thumbnailURL` / `image` / `contractTerms.fileURL`) — poll this endpoint and retry once both values appear. Hash/CID calculation runs asynchronously after upload and normally completes within a few minutes.
{% endhint %}

#### Headers

| Name                                               | Type   | Description                       |
| -------------------------------------------------- | ------ | --------------------------------- |
| commerce-api-key<mark style="color:red;">\*</mark> | string | Commerce API Key                  |
| issuer-address<mark style="color:red;">\*</mark>   | string | Contract Address of API Key owner |

#### Request Body

| Name                                                    | Type   | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                   |
| ------------------------------------------------------- | ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| payload<mark style="color:red;">\*</mark>               | array  | Array of the request. Each element of the array corresponds to a file.                                                                                                                                                                                                                                                                                                                                                                                        |
| payload\[\*].filename<mark style="color:red;">\*</mark> | string | <p>Name of the file. Should contain file extensions (.jpg, .png, .pdf, etc).</p><p>Should not contain space. Use a unique name for each file.</p><p>If the filename is already used by the client, then it will fail when uploading. (unique for each LUW, but can be same for different LUW).</p><p>This is to avoid modification of files that are already used in SRR.</p><p>If the client needs to delete an existing file, please contact Startbahn.</p> |
| payload\[\*].category<mark style="color:red;">\*</mark> | string | <p>Please refer to the <a href="/pages/0iLTiPfX9AyKSw5AoPRc">category table</a> for how each category is treated, and <a href="https://help.port.startrail.io/hc/en-us/articles/7720691967767-What-is-an-Artwork-File-">this page</a> for product-level background.</p><p>Use <code>non\_attachment\_file</code> to upload thumbnails, images and contract terms. Use the same category here that you used when generating the signed URL.</p>                |

{% tabs %}
{% tab title="200: OK " %}
The API responds with 200 if the check to the storage provider success. Bear in mind that client’s back end needs to check the `results[*].message` if there is a problem with each file.

{% tabs %}
{% tab title="Body" %}

<table><thead><tr><th width="211">Body Attribute</th><th width="356">Description</th><th width="253">Format</th></tr></thead><tbody><tr><td>payload</td><td>Array of the results. The order of the array is same as the request.</td><td>array</td></tr><tr><td>payload[*].filename</td><td>The value will be same as parameter sent in request.</td><td>string</td></tr><tr><td>payload[*].hash</td><td>The value of calculated hash. If the hash still in calculation, file is not ready yet. If the file size is above 20 GB, you need to contact to Startbahn to calculate hash manually.</td><td>string</td></tr><tr><td>payload[*].cid</td><td>The value of IPFS CID v1. Like the <code>hash</code>, ones for very large files need to be contacted to Startbahn.</td><td>string</td></tr><tr><td>payload[*].size</td><td>The size in bytes.</td><td>number</td></tr><tr><td>payload[*].message</td><td>Exist if there is problem describing it.</td><td>string</td></tr></tbody></table>
{% endtab %}

{% tab title="Example" %}

```
// Example result
{
  "results": [
    // correct result
    {
      "filename": "test.jpg",
      "hash": "sha256-a63238ce3b8c4f8a99fb453d716d5451f75508c2e403a58af0412014187e7a61",
      "cid": "bafkreiehikh4kiuahuyqmxt3zy6pap7eouewmmpf4b5326qp3zqmjtzfy4"
      "size": 712
    },
    // if hash calculation is not yet finished
    {
      "filename": "hash-not-yet-calulated.jpg",
      "message": "HASH_STILL_IN_CALCULATION"
    }
  ]
}
```

{% endtab %}
{% endtabs %}
{% endtab %}

{% tab title="400: Bad Request " %}
The API responds with 400 if the request body is invalid.

```javascript
// If payload is invalid
{
  "statusCode": 400,
  "message": [
    "payload.0.category must be one of the following values: certificate,for_authenticity,artwork,installation,non_attachment_file"
  ]
}
```

{% endtab %}

{% tab title="500: Internal Server Error " %}
The API responds with 5xx if there is an issue between the network or storage provider, currently, it uses Google Cloud Storage.

```
// If the network is error or unknown error. Startbahn side need to check.
{
  "statusCode": 502,
  "message": "Bad Gateway"
}
```

{% endtab %}

{% tab title="404: Not Found " %}
{% code fullWidth="true" %}

```json
// If one or more files are not found
{
  "statusCode": 404,
  "message": [
    {
      "index": 0,
      "filePath": "srr-images/0x0000000000000000000000000000000000000000/not-found.png"
    }
  ]   
}
```

{% endcode %}
{% endtab %}
{% endtabs %}

## Swagger Endpoint (Test Environment)

[Swagger to test](https://api-stg.startrail.startbahn.jp/port/api#/public/CommerceController_bulkGetFileMetadata)

## Required Permissions

Check the [parent page](/issue-transfer-api/issue-and-transfer-srr-nft).

### Request Body Example

```json
// Example for thumbnail
{
  "payload": [
    {
      "filename": "thumbnail.jpg",
      "category": "non_attachment_file"
    }
  ]
}

// Example for contract terms
{
  "payload": [
    {
      "filename": "contract.pdf",
      "category": "non_attachment_file"
    }
  ]
}

// Example for other categories
{
  "payload": [
    {
      "filename": "certificate.pdf",
      "category": "certificate"
    }
  ]
}
```

## Code Example

Check [parent page](/issue-transfer-api/issue-and-transfer-srr-nft).


# Issue & Transfer

to issue or issue+transfer SRR

<mark style="color:green;">`POST`</mark> `<base_url>/port/api/v1/commerce/srrs`

Please replace `<base_url>` as explained [here](/readme/url-per-environment).

{% hint style="success" %}
**Looking for the flow, a payload to copy, or decoding an error?**

* [Issuance Flow (End-to-End)](/issue-transfer-api/issue-and-transfer-srr-nft/issuance-flow) — flowcharts for single/bulk issuance with new or reused files.
* [Request Payloads & Recipes](/issue-transfer-api/issue-and-transfer-srr-nft/payloads) — ready-to-use bodies for every scenario (issue to self, issue-on-buyer, custom collection, NFC tags, attachments, bulk).
* [Errors & Troubleshooting](/issue-transfer-api/issue-and-transfer-srr-nft/errors) — every status code and message this endpoint can return, with causes and fixes.
  {% endhint %}

### Precaution

#### Multiple issuance

{% hint style="warning" %}
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.
{% endhint %}

#### issue to issuer

{% hint style="info" %}
If payload\[\*].to is not given, your SRR will be issued to your `luw` address
{% endhint %}

#### File URLs — every file must come from the signed-URL flow

{% hint style="danger" %}
**All file URLs in an issuance request must be the `finalUrl` returned by** [**`/commerce/signedUrls`**](/issue-transfer-api/issue-and-transfer-srr-nft/request-signed-url-for-upload-download-files)**.** 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.thumbnailURL`
* `payload[*].metadata.image`
* `payload[*].metadata.contractTerms.fileURL`
* `payload[*].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](/issue-transfer-api/issue-and-transfer-srr-nft/errors#request-level-errors-400)).

The full flow for every file (thumbnail, image, contract terms, attachments):

1. [Request a signed URL](/issue-transfer-api/issue-and-transfer-srr-nft/request-signed-url-for-upload-download-files) (`action: "write"`) — keep the returned `finalUrl`
2. Upload the file with HTTP `PUT` to the signed URL
3. [Poll `/commerce/fileMetadata`](/issue-transfer-api/issue-and-transfer-srr-nft/file-information-metadata) until the file's `hash` **and** `cid` are returned
4. Use the `finalUrl` in `metadata.thumbnailURL` / `image` / `contractTerms.fileURL` or `attachmentFiles[*].url`

Note: `metadata.external_url` is exempt — it is an external reference link by design and is never uploaded to IPFS.
{% endhint %}

{% hint style="warning" %}
**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](/issue-transfer-api/issue-and-transfer-srr-nft/request-signed-url-for-upload-download-files): **`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.
{% endhint %}

#### Headers

| Name                                               | Type   | Description                       |
| -------------------------------------------------- | ------ | --------------------------------- |
| commerce-api-key<mark style="color:red;">\*</mark> | string | Commerce API Key                  |
| issuer-address<mark style="color:red;">\*</mark>   | string | Contract Address of API Key owner |

#### Request Body

| Name                                                                | Type           | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            |
| ------------------------------------------------------------------- | -------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| requestId                                                           | string         | <p>A <code>requestId</code> given by the caller, to ensure requests are only processed once. If the <code>requestId</code> is known and processed before, api will not process this call again, and respond with an error. A good practice is using random UUID.</p><p>\*<code>requestId</code> must be unique for a given <code>issuer-address</code>. As a result any duplicate combination of <code>requestId</code> and <code>issuer-address</code> is instantly rejected with no impact on either.</p>                                                                                                                                                                                                            |
| payload<mark style="color:red;">\*</mark>                           | array          | Array of issue requests. Further constraints explained [above](#multiple-issuance).                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    |
| payload\[\*].externalId<mark style="color:red;">\*</mark>           | 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<mark style="color:red;">\*</mark>             | object         | <p>The metadata to be issued as a complex object. Detailed schema specification can be found <a href="/pages/cfyaLly9LtHuauzFV359">here</a>.</p><p>The API accepts versions 2.0 and higher.</p>                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        |
| payload\[\*].artistAddress<mark style="color:red;">\*</mark>        | string         | The ethereum address of the artist of the artwork.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     |
| payload\[\*].isPrimaryIssuer<mark style="color:red;">\*</mark>      | boolean        | If you are the primary issuer of this NFT, set this to true.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           |
| payload\[\*].lockExternalTransfer<mark style="color:red;">\*</mark> | boolean        | If you want to prevent your NFTs to be transferred on decentralized marketplaces, set this to true.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    |
| payload\[\*].to                                                     | string         | <p>Ethereum address target the NFT should be sent to after minting (Issue on Buyer).</p><p>If none is given the NFT will be minted into your LUW by default.</p>                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       |
| payload\[\*].attachmentFiles                                        | Array\<object> | Attachment files that will be included in SRR.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         |
| payload\[\*].attachmentFiles\[\*].name                              | string         | <p>The name of file.</p><p>This is used for when the file is downloaded or shown. The extension is recommended to be the same as the actual uploaded file.</p>                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         |
| payload\[\*].attachmentFiles\[\*].category                          | string         | <p>Please refer to <a href="https://help.port.startrail.io/hc/en-us/articles/7720691967767-What-is-an-Artwork-File-">this page</a> to understand the difference among the categories.</p><p>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)</p>                                                                                                                                                                                                                                                                                                                                                     |
| payload\[\*].attachmentFiles\[\*].url                               | string         | <p>The URL must be under Startbahn's GCS bucket. Use the <code>finalUrl</code> field returned by the <a href="/pages/0iLTiPfX9AyKSw5AoPRc">signed URL endpoint</a>, or a URL that Startbahn has pre-provisioned for you. Any other URL is rejected without issuing the SRR.</p><p>For each <code>attachmentFiles</code> entry, the API resolves the URL to its GCS path, reads the precomputed SHA-256 from the storage object metadata, and folds <code>{category, hash}</code> entries into <code>metadata.digitalComponents</code> (<code>artwork</code> category) or <code>metadata.attachmentFiles</code> (other categories) before issuance. You do not need to populate those metadata sub-fields yourself.</p> |
| 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> | <p>Deprecated. Use the <code>external\_url</code> field in metadata.</p><p>To remain backward compatible the external URLs is still allowed and is simply passing the first string in the array to the metadata as <code>external\_url</code>). This mapping is redundant and will be removed.</p>                                                                                                                                                                                                                                                                                                                                                                                                                     |

{% tabs %}
{% tab title="201: Created " %}
The API responds with 201. see Response Body results\[\*].status for details of each entry.

{% tabs %}
{% tab title="Body" %}

<table><thead><tr><th>Body Attribute</th><th width="259.66666666666663">Description</th><th>Format</th></tr></thead><tbody><tr><td>results</td><td>results of the request</td><td>Array</td></tr><tr><td>results[*].srr</td><td>Minimal identifying details of the issued SRR. Richer information (issuer, artist, collection details, on-chain metadata, history, ownership) must be queried from the <a href="/pages/DEDTJpFrrjYII6dFBDUe">subgraph</a> once the transaction is mined.</td><td>object</td></tr><tr><td>results[*].srr.tokenId</td><td>NFT token ID of the SRR.</td><td>string</td></tr><tr><td>results[*].srr.metadataCID</td><td>IPFS CID of the SRR metadata. Resolve via the Startrail IPFS CDN gateway: <code>https://cdn.startrail.io/ipfs/&#x3C;cid></code>. See <a href="/pages/nsrhLznHxyKf7AAMT1A0">IPFS CDN gateway</a>.</td><td>string</td></tr><tr><td>results[*].srr.metadataURL</td><td>Convenience HTTPS URL that resolves the SRR metadata via the Startrail IPFS CDN gateway. Same content as <code>metadataCID</code> resolved through <code>https://cdn.startrail.io/ipfs/</code>.</td><td>string</td></tr><tr><td>results[*].srr.collectionContractAddress</td><td>Collection contract address the SRR belongs to. <code>null</code> if the SRR was not issued under a custom collection.</td><td>string | null</td></tr><tr><td>results[*].externalId</td><td>ID to identify the SRR. Defined by the client when calling.</td><td>string</td></tr><tr><td>results[*].status</td><td><code>waiting_for_mining</code> – wait for completion of blockchain mining. The status (and any further SRR data) can then be confirmed via the <a href="/pages/DEDTJpFrrjYII6dFBDUe">subgraph</a> or less favorably via the <a href="/pages/qlXBPGW7iA7bCtQeBfrc">Get SRR by Token Id</a> REST endpoint.</td><td>string</td></tr></tbody></table>

{% hint style="info" %}
**Response shape change**

The response intentionally returns only the minimum identifiers needed to track the issued SRR (`tokenId`, `metadataCID`, `metadataURL`, `collectionContractAddress`). Fields previously returned by this endpoint such as `srr.issuer`, `srr.artist`, `srr.collection.{name,symbol}`, `srr.isPrimaryIssuer`, `srr.issuedAt`, `srr.metadata.{json,originalJson,digest}`, `srr.createdAt` and `srr.updatedAt` are no longer part of the issuance response.

To read SRR data after issuance:

* Use the [subgraph](/subgraph/subgraph) for issuer, artist, collection, ownership, history and provenance information (authoritative source).
* Use the [Startrail IPFS CDN gateway](/subgraph/ipfs-cdn-gateway) (`https://cdn.startrail.io/ipfs/<cid>`) to fetch the SRR metadata JSON, attachment files and images by their CID.
  {% endhint %}
  {% endtab %}

{% tab title="Example" %}

```json
{
  "results": [
    {
      "status": "waiting_for_mining",
      "srr": {
        "tokenId": "227890056407",
        "metadataCID": "bafkreiabepvyxyetkcb3xbjo3ocuyfo6psv3rc3yj34hequwcsilyvjima",
        "metadataURL": "https://cdn.startrail.io/ipfs/bafkreiabepvyxyetkcb3xbjo3ocuyfo6psv3rc3yj34hequwcsilyvjima",
        "collectionContractAddress": "0x7942627305545AF0e6c826d54cC9b2c5D190A874"
      },
      "externalId": "c0456840-5bdf-4375-a6ce-106697b7dfb7"
    }
  ]
}
```

{% endtab %}
{% endtabs %}
{% endtab %}

{% tab title="400: Bad Request " %}
The API responds with 400 if request body invalid or some validation errors that can be instantly detected.

```json
// If the metadata is same with another previous SRR
{
  "statusCode": 400,
  "message": {
    "results": [
      {
        "status": "failed",
        "externalId": "c0456840-5bdf-4375-a6ce-106697b7dfb7",
        "message": "DUPLICATE_METADATA"
      }
    ]
  }
}

{
  "statusCode": 400,
  "message": {
    "results": [
      {
        "status": "failed",
        "externalId": "c0456840-5bdf-4375-a6ce-106697b7dfb7",
        "message": "DUPLICATE_CHIP"
      }
    ]
  }
}


{
    "statusCode": 400,
    "message": "Request Content Error: Current request already logged with status successful"
}


{
    "statusCode": 400,
    "message": {
        "results": [
            {
                "status": "failed",
                "externalId": "bulk20122fdredcd2dfve",
                "message": "UNKNOWN_ERROR: external ID: bulk20122fdredcd2dfve."
            }
        ]
    }
}

{
  "statusCode": 400,
  "message": {
    "results": [
      {
        "status": "failed",
        "externalId": "c0456840-5bdf-4375-a6ce-106697b7dfb7",
        "message": "UNKNOWN_ARTIST"
      }
    ]
  }
}

// If metadata with issuer and artist combination is already sent
{
  "statusCode": 400,
  "message": "Request Content Error: Current request already logged with status successful"
}

// DTO validation error
{
    "statusCode": 400,
    "message": [
        "payload.0.metadata is invalid. Check it against the metadata JSON schema. Details:  should have required property 'title' ({\"missingProperty\":\"title\"}).",
        "payload.0.artistAddress must be a valid Ethereum addresses",
        "payload.0.externalId should not be empty",
        "payload.0.externalId must be a string"
    ]
}
```

#### 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**

```json
{
    "statusCode": 400,
    "message": {
        "results": [
            {
                "status": "failed",
                "externalId": "anId",
                "message": "DUPLICATE_METADATA"
            },
            {
                "status": "failed",
                "externalId": "anId",
                "message": "SRR_NOT_CREATED: Not created because there is SRR in the same batch that error"
            }
        ]
    }
}
```

**Example of DTO validation error**

```json
{
    "statusCode": 400,
    "message": [
        "payload.1.metadata is invalid. Check it against the metadata JSON schema. Details:  should have required property 'title' ({\"missingProperty\":\"title\"}).",
        "payload.1.artistAddress must be a valid Ethereum addresses",
        "payload.1.externalId should not be empty",
        "payload.1.externalId must be a string"
    ]
}
```

{% endtab %}

{% tab title="500: Internal Server Error " %}
The API responds with 5xx if there are other issues, such as deeper validation errors.

```json
// For example in case of collection ownership issue
{
  "statusCode": 500,
  "message": "STARTRAIL_ERROR: <reason>"
}

// In case there is unknown error. Please contact us.
{
  "statusCode": 500,
  "message": "UNKNOWN_ERROR: external ID: xxxx"
}
```

{% endtab %}
{% endtabs %}

## Swagger Endpoint (Test Environment)

[Swagger to test](https://api-stg.startrail.startbahn.jp/port/api#/public/CommerceController_)

## Required Permissions

Check the [parent page](/issue-transfer-api/issue-and-transfer-srr-nft).

## Request Body Example

```json
{
  "requestId": "0004f572-7769-4b8b-8108-a13a36cd88d4",
  "payload": [
    {
      "externalId": "0004f572-7769-4b8b-8108-a13a36cd88d4",
      "metadata": {
        "$schema": "https://api.startrail.io/api/v1/schema/registry-record-metadata.v2.1.schema.json",
        "$schemaIntegrity": "sha256-15f8e99eb9d4292287282942db2f2de9bbcc4761c555c6f7da23feec010c1221",
        "title": {
          "en": "A title",
          "ja": "タイトル",
          "zh": "一个标题"
        },
        "size": {
          "width": 200,
          "height": 400,
          "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": "https://static-files-stg.startrail.startbahn.jp/srr-images/contract-terms.pdf"
        },
        "note": {
          "en": "note",
          "zh": "注意"
        },
        "thumbnailURL": "https://static-files-stg.startrail.startbahn.jp/srr-images/thumbnail-example.jpg",
        "yearOfCreation": {
          "en": "around 2010-2020",
          "ja": "2010年から2020年頃"
        },
        "isDigital": true,
        "name": "some nft name",
        "description": "some nft description",
        "image": "https://static-files-stg.startrail.startbahn.jp/srr-images/thumbnail-example.jpg",
        "external_url": "https://openseacreatures.io/3"
      },
      "artistAddress": "0x36E9f4C26357FDb14AdF939a12AdBba92a209C01",
      "isPrimaryIssuer": true,
      "lockExternalTransfer": false,
      "to": "0x36E9f4C26357FDb14AdF939a12AdBba92a209C01",
      "collectionAddress": "0xfbF4C1A1eb4258aE0F74807f6c1e854918DC8ed3",
      "attachmentFiles": [
        {
          "name": "image-example.jpg",
          "url": "https://static-files-stg.startrail.startbahn.jp/srr-images/image-example.jpg",
          "category": "artwork"
        },
        {
          "name": "certificate-example.jpg",
          "url": "https://static-files-stg.startrail.startbahn.jp/srr-images/certificate-example.jpg",
          "category": "certificate"
        },
        {
          "name": "for_authenticity.jpg",
          "url": "https://static-files-stg.startrail.startbahn.jp/srr-images/for_authenticity.jpg",
          "category": "for_authenticity"
        },
        {
          "name": "installation.jpg",
          "url": "https://static-files-stg.startrail.startbahn.jp/srr-images/installation.jpg",
          "category": "installation"
        }
      ]
    }
  ]
}
```

## Code Example

Check [parent page](/issue-transfer-api/issue-and-transfer-srr-nft).

{% hint style="success" %}
**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:

```javascript
"chipUIDs": [
    "1234567890abcdef"
],
"startbahnCertICTagUIDs": [
    "1234567890abcdef"
],
```

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!`](/issue-transfer-api/issue-and-transfer-srr-nft/errors#nfc-tags-and-quota-errors). For a copy-paste example see [NFC tags recipe](/issue-transfer-api/issue-and-transfer-srr-nft/payloads#recipes).
{% endhint %}


# Request Payloads & Recipes

Copy-paste request payloads for every Issue & Transfer scenario — issue to self, issue-on-buyer, custom collection, NFC tags, attachments and bulk.

Ready-to-use request bodies for `POST <base_url>/port/api/v1/commerce/srrs`. Every example here is **valid against SRR metadata schema v2.2**. For the full field reference and response shapes see [Issue & Transfer](/issue-transfer-api/issue-and-transfer-srr-nft/issue-and-transfer); for failures see [Errors & Troubleshooting](/issue-transfer-api/issue-and-transfer-srr-nft/errors).

{% hint style="info" %}
The file URLs in these examples (`thumbnailURL`, `image`, `contractTerms.fileURL`, `attachmentFiles[*].url`) show the shape of the **`finalUrl` returned by** [**`/commerce/signedUrls`**](/issue-transfer-api/issue-and-transfer-srr-nft/request-signed-url-for-upload-download-files). Always use that returned value verbatim — it is available immediately in the signed-URL response, before the upload itself.
{% endhint %}

{% hint style="info" %}
All requests require the headers `commerce-api-key`, `issuer-address`, `Content-Type: application/json` — see the [Issue & Transfer SRR overview](/issue-transfer-api/issue-and-transfer-srr-nft#required-headers).
{% endhint %}

## Body shape

```json
{
  "requestId": "<optional unique UUID>",
  "payload": [ /* one or more SRR entries */ ]
}
```

| Field       | Type     | Required     | Description                                                                                                                                                                |
| ----------- | -------- | ------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `requestId` | `string` | optional     | Unique idempotency key (UUID recommended). A reused value is rejected — see [idempotency](/issue-transfer-api/issue-and-transfer-srr-nft/errors#request-level-errors-400). |
| `payload`   | `array`  | **required** | 1+ SRR entries. Minimum 1; **no hard maximum** (≤ 250 recommended for stable processing).                                                                                  |

### `payload[*]` fields

| Field                  | Type               | Required     | Description                                                                                               |
| ---------------------- | ------------------ | ------------ | --------------------------------------------------------------------------------------------------------- |
| `externalId`           | `string`           | **required** | Your unique reference for this SRR (UUID recommended). Echoed back in the response.                       |
| `metadata`             | `object`           | **required** | SRR metadata, schema v2.0+. See [SRR metadata v2.2](/metadata-schema/startrail-registry-srr/version-2.2). |
| `isPrimaryIssuer`      | `boolean`          | **required** | `true` if you are the primary issuer.                                                                     |
| `lockExternalTransfer` | `boolean`          | **required** | `true` to disable standard ERC-721 transfer methods.                                                      |
| `artistAddress`        | `string` (EOA/LUW) | **required** | Artist's Ethereum address. Must be a known LUW.                                                           |
| `to`                   | `string` (EOA)     | optional     | Recipient — SRR is transferred here after minting. Omit to keep it in your LUW.                           |
| `collectionAddress`    | `string`           | optional     | Custom collection contract to mint into (must be owned by `issuer-address`).                              |
| `attachmentFiles`      | `array`            | optional     | Previously uploaded files; folded into metadata server-side. See [recipe](#issue-with-attachment-files).  |
| `externalUrls`         | `string[]`         | optional     | Only the **first** URL is used (becomes `metadata.external_url`).                                         |

## Recipes

{% tabs %}
{% tab title="Minimal (to self)" %}
Issue a single SRR into your own LUW (no `to`, no collection).

```json
{
  "payload": [
    {
      "externalId": "8f1d4c2a-0e3b-4f7a-9c11-aa0000000001",
      "isPrimaryIssuer": true,
      "lockExternalTransfer": false,
      "artistAddress": "0xA6E6a9E20a541680a1D6E1412f5088AefBF58a22",
      "metadata": {
        "$schema": "https://api.startrail.io/api/v1/schema/registry-record-metadata.v2.2.schema.json",
        "name": "Sunrise No. 1",
        "title": { "en": "Sunrise No. 1", "ja": "日の出 No.1" },
        "medium": { "en": "Oil on canvas" },
        "yearOfCreation": { "en": "2024" },
        "thumbnailURL": "https://static-files-stg.startrail.startbahn.jp/srr-images/artwork-thumbnail.jpg",
        "image": "https://static-files-stg.startrail.startbahn.jp/srr-images/artwork-thumbnail.jpg"
      }
    }
  ]
}
```

{% endtab %}

{% tab title="Issue on buyer" %}
Set `to` to mint and transfer to a buyer's EOA in one call.

```json
{
  "requestId": "req-2026-06-26-001",
  "payload": [
    {
      "externalId": "8f1d4c2a-0e3b-4f7a-9c11-aa0000000002",
      "isPrimaryIssuer": true,
      "lockExternalTransfer": false,
      "artistAddress": "0xA6E6a9E20a541680a1D6E1412f5088AefBF58a22",
      "to": "0x1111111111111111111111111111111111111111",
      "metadata": {
        "$schema": "https://api.startrail.io/api/v1/schema/registry-record-metadata.v2.2.schema.json",
        "name": "Blue Composition",
        "title": { "en": "Blue Composition" },
        "medium": { "en": "Acrylic on panel" },
        "yearOfCreation": { "en": "2023" },
        "thumbnailURL": "https://static-files-stg.startrail.startbahn.jp/srr-images/artwork-thumbnail.jpg",
        "image": "https://static-files-stg.startrail.startbahn.jp/srr-images/artwork-thumbnail.jpg"
      }
    }
  ]
}
```

{% endtab %}

{% tab title="Custom collection" %}
Set `collectionAddress` to mint into a [collection](/issue-transfer-api/collection/create-collection) you own.

```json
{
  "payload": [
    {
      "externalId": "8f1d4c2a-0e3b-4f7a-9c11-aa0000000003",
      "isPrimaryIssuer": true,
      "lockExternalTransfer": true,
      "artistAddress": "0xA6E6a9E20a541680a1D6E1412f5088AefBF58a22",
      "collectionAddress": "0x4412Ba95BEC0CDB4562D97CB9149575Ea5B514A5",
      "metadata": {
        "$schema": "https://api.startrail.io/api/v1/schema/registry-record-metadata.v2.2.schema.json",
        "name": "Collection Piece A",
        "title": { "en": "Collection Piece A" },
        "medium": { "en": "Mixed media" },
        "yearOfCreation": { "en": "2025" },
        "thumbnailURL": "https://static-files-stg.startrail.startbahn.jp/srr-images/artwork-thumbnail.jpg",
        "image": "https://static-files-stg.startrail.startbahn.jp/srr-images/artwork-thumbnail.jpg"
      }
    }
  ]
}
```

{% endtab %}

{% tab title="NFC tags" %}
Attach IC tag UIDs. In **v2.2**, `chipUIDs` and `startbahnCertICTagUIDs` are **mutually required** and must hold the **same values**. List multiple UIDs to tag one artwork with multiple chips.

```json
{
  "payload": [
    {
      "externalId": "8f1d4c2a-0e3b-4f7a-9c11-aa0000000004",
      "isPrimaryIssuer": true,
      "lockExternalTransfer": false,
      "artistAddress": "0xA6E6a9E20a541680a1D6E1412f5088AefBF58a22",
      "metadata": {
        "$schema": "https://api.startrail.io/api/v1/schema/registry-record-metadata.v2.2.schema.json",
        "name": "Tagged Sculpture",
        "title": { "en": "Tagged Sculpture", "ja": "タグ付き彫刻" },
        "medium": { "en": "Bronze" },
        "yearOfCreation": { "en": "2022" },
        "thumbnailURL": "https://static-files-stg.startrail.startbahn.jp/srr-images/artwork-thumbnail.jpg",
        "image": "https://static-files-stg.startrail.startbahn.jp/srr-images/artwork-thumbnail.jpg",
        "chipUIDs": ["04A1B2C3D4E580", "04F6E7D8C9BA00"],
        "startbahnCertICTagUIDs": ["04A1B2C3D4E580", "04F6E7D8C9BA00"]
      }
    }
  ]
}
```

{% hint style="warning" %}
Each IC tag UID consumes one unit of the issuer LUW's NFC-tag allowance. Exceeding it returns `LUW missing NFC tag!` — see [NFC tag errors](/issue-transfer-api/issue-and-transfer-srr-nft/errors#nfc-tags-and-quota-errors).
{% endhint %}
{% endtab %}

{% tab title="Attachment files" %}
Reference files already uploaded via [`/signedUrls`](/issue-transfer-api/issue-and-transfer-srr-nft/request-signed-url-for-upload-download-files). The server resolves each URL, reads its hash, and folds it into `metadata.digitalComponents` (`artwork`) or `metadata.attachmentFiles` (other categories).

```json
{
  "payload": [
    {
      "externalId": "8f1d4c2a-0e3b-4f7a-9c11-aa0000000005",
      "isPrimaryIssuer": true,
      "lockExternalTransfer": false,
      "artistAddress": "0xA6E6a9E20a541680a1D6E1412f5088AefBF58a22",
      "externalUrls": ["https://gallery.example.com/works/123"],
      "attachmentFiles": [
        { "name": "certificate.pdf", "url": "https://static-files-stg.startrail.startbahn.jp/srr-images/certificate.pdf", "category": "certificate" },
        { "name": "hires-artwork.png", "url": "https://static-files-stg.startrail.startbahn.jp/srr-images/hires-artwork.png", "category": "artwork" }
      ],
      "metadata": {
        "$schema": "https://api.startrail.io/api/v1/schema/registry-record-metadata.v2.2.schema.json",
        "name": "Documented Work",
        "title": { "en": "Documented Work" },
        "medium": { "en": "Photography" },
        "yearOfCreation": { "en": "2021" },
        "thumbnailURL": "https://static-files-stg.startrail.startbahn.jp/srr-images/artwork-thumbnail.jpg",
        "image": "https://static-files-stg.startrail.startbahn.jp/srr-images/artwork-thumbnail.jpg"
      }
    }
  ]
}
```

{% hint style="info" %}
`category` here accepts `certificate`, `for_authenticity`, `artwork`, `installation`. Wait until the file's hash is computed (poll [`/fileMetadata`](/issue-transfer-api/issue-and-transfer-srr-nft/file-information-metadata)) — otherwise issuance returns `Attachment hash not ready …`.
{% endhint %}

{% hint style="warning" %}
The same rule applies to `metadata.thumbnailURL`, `metadata.image` and `metadata.contractTerms.fileURL`: they must be `finalUrl`s from the [signed-URL flow](/issue-transfer-api/issue-and-transfer-srr-nft/request-signed-url-for-upload-download-files) (or `ipfs://` URLs) with their `cid` already computed — external hosts are rejected. See [File URLs](/issue-transfer-api/issue-and-transfer-srr-nft/issue-and-transfer#file-urls-every-file-must-come-from-the-signed-url-flow).
{% endhint %}
{% endtab %}

{% tab title="Bulk (mixed)" %}
One request, multiple SRRs mixing scenarios. Remember: **the batch is atomic** — if any entry fails, none are issued.

<details>

<summary>Multi-item bulk payload</summary>

```json
{
  "requestId": "batch-2026-06-26-A",
  "payload": [
    {
      "externalId": "bulk-001-self",
      "isPrimaryIssuer": true,
      "lockExternalTransfer": false,
      "artistAddress": "0xA6E6a9E20a541680a1D6E1412f5088AefBF58a22",
      "metadata": {
        "$schema": "https://api.startrail.io/api/v1/schema/registry-record-metadata.v2.2.schema.json",
        "name": "Batch Piece 1",
        "title": { "en": "Batch Piece 1" },
        "medium": { "en": "Watercolor" },
        "yearOfCreation": { "en": "2020" },
        "thumbnailURL": "https://static-files-stg.startrail.startbahn.jp/srr-images/artwork-thumbnail.jpg",
        "image": "https://static-files-stg.startrail.startbahn.jp/srr-images/artwork-thumbnail.jpg"
      }
    },
    {
      "externalId": "bulk-002-to-buyer",
      "isPrimaryIssuer": false,
      "lockExternalTransfer": false,
      "artistAddress": "0xA6E6a9E20a541680a1D6E1412f5088AefBF58a22",
      "to": "0x2222222222222222222222222222222222222222",
      "metadata": {
        "$schema": "https://api.startrail.io/api/v1/schema/registry-record-metadata.v2.2.schema.json",
        "name": "Batch Piece 2",
        "title": { "en": "Batch Piece 2", "ja": "バッチ作品2" },
        "medium": { "en": "Digital print" },
        "yearOfCreation": { "en": "2026" },
        "thumbnailURL": "https://static-files-stg.startrail.startbahn.jp/srr-images/artwork-thumbnail.jpg",
        "image": "https://static-files-stg.startrail.startbahn.jp/srr-images/artwork-thumbnail.jpg"
      }
    },
    {
      "externalId": "bulk-003-collection-tags",
      "isPrimaryIssuer": true,
      "lockExternalTransfer": true,
      "artistAddress": "0xA6E6a9E20a541680a1D6E1412f5088AefBF58a22",
      "collectionAddress": "0x4412Ba95BEC0CDB4562D97CB9149575Ea5B514A5",
      "metadata": {
        "$schema": "https://api.startrail.io/api/v1/schema/registry-record-metadata.v2.2.schema.json",
        "name": "Batch Piece 3",
        "title": { "en": "Batch Piece 3" },
        "medium": { "en": "Sculpture" },
        "yearOfCreation": { "en": "2019" },
        "thumbnailURL": "https://static-files-stg.startrail.startbahn.jp/srr-images/artwork-thumbnail.jpg",
        "image": "https://static-files-stg.startrail.startbahn.jp/srr-images/artwork-thumbnail.jpg",
        "chipUIDs": ["0400112233AABB"],
        "startbahnCertICTagUIDs": ["0400112233AABB"]
      }
    }
  ]
}
```

</details>
{% endtab %}
{% endtabs %}

## Rules to remember

{% hint style="info" %}

* **Required metadata fields (v2.2):** `$schema`, `name`, `title`, `thumbnailURL`, `image`, `medium`, `yearOfCreation`.
* **All file URLs must come from the signed-URL flow.** `thumbnailURL`, `image`, `contractTerms.fileURL` and `attachmentFiles[*].url` must be the `finalUrl`s returned by [`/commerce/signedUrls`](/issue-transfer-api/issue-and-transfer-srr-nft/request-signed-url-for-upload-download-files). External URLs are **rejected with 400** — see [File URLs](/issue-transfer-api/issue-and-transfer-srr-nft/issue-and-transfer#file-urls-every-file-must-come-from-the-signed-url-flow). (`external_url` is exempt.)
* **Metadata files must be public.** Upload `thumbnailURL` / `image` / `contractTerms.fileURL` files with a **public category** — `non_attachment_file` (recommended) or `artwork`. Private-category files (`certificate`, `for_authenticity`, `installation`) are not accessible to viewers and are rejected for these fields.
* **NFC tags:** include `chipUIDs` **and** `startbahnCertICTagUIDs` with identical values.
* **Digital works:** if `isDigital: true`, `digitalComponents` is required.
* **No hard batch cap**, but keep `payload` ≤ 250 entries for stable processing.
* **Atomic batches:** one failed entry cancels the whole request.
* Need the response shape and per-field details? → [Issue & Transfer](/issue-transfer-api/issue-and-transfer-srr-nft/issue-and-transfer). Hit an error? → [Errors & Troubleshooting](/issue-transfer-api/issue-and-transfer-srr-nft/errors).
  {% endhint %}


# Errors & Troubleshooting

Every error the Issue & Transfer SRR API family can return — HTTP status, exact message, cause, and how to fix it.

This page catalogues **every error** returned by the Commerce REST endpoints used to issue, transfer and manage SRRs:

* `POST /port/api/v1/commerce/srrs` — [Issue & Transfer](/issue-transfer-api/issue-and-transfer-srr-nft/issue-and-transfer)
* `POST /port/api/v1/commerce/collection` — [Create Collection](/issue-transfer-api/collection/create-collection)
* `POST /port/api/v1/commerce/signedUrls` — [Request Signed URL](/issue-transfer-api/issue-and-transfer-srr-nft/request-signed-url-for-upload-download-files)
* `POST /port/api/v1/commerce/fileMetadata` — [File Information Metadata](/issue-transfer-api/issue-and-transfer-srr-nft/file-information-metadata)

{% hint style="info" %}
Replace `<base_url>` with the environment you are calling — see [URL per environment](/readme/url-per-environment). All examples below use the JSON response envelope described next.
{% endhint %}

## Response envelope

Errors come back in one of **two shapes**:

{% tabs %}
{% tab title="Request-level" %}
A single error for the whole request (bad headers, invalid body, duplicate request, internal error):

```json
{
  "statusCode": 400,
  "message": "Address not found: 0xabc...123"
}
```

`message` is either a **string** or an **array of strings** (when multiple validation rules fail at once).
{% endtab %}

{% tab title="Per-SRR (issuance)" %}
For `POST /srrs`, issuance problems are reported **per payload entry** under `message.results[]`, keyed by your `externalId`:

```json
{
  "statusCode": 400,
  "message": {
    "results": [
      {
        "status": "failed",
        "externalId": "c0456840-5bdf-4375-a6ce-106697b7dfb7",
        "message": "DUPLICATE_METADATA"
      }
    ]
  }
}
```

{% endtab %}
{% endtabs %}

{% hint style="warning" %}
**Issuance is atomic.** In a bulk `POST /srrs`, if **any** entry fails, the **entire batch is cancelled** — no SRR in the request is issued. Fix the failing entry and resubmit the whole payload.
{% endhint %}

## Status code overview

| Status                      | Meaning                                                                                 | Typical causes                                                                                   |
| --------------------------- | --------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------ |
| `400` Bad Request           | The request was rejected before/at validation, or an SRR entry failed an issuance check | Invalid metadata, bad address, duplicate request, [per-SRR codes](#per-srr-issuance-error-codes) |
| `401` Unauthorized          | Authentication failed                                                                   | Missing/invalid `commerce-api-key` or `issuer-address`                                           |
| `404` Not Found             | A referenced file does not exist in storage                                             | `POST /fileMetadata` for a file that was never uploaded                                          |
| `500` Internal Server Error | A downstream (Startrail / signing / storage) step failed                                | Quota/NFC limits, `STARTRAIL_ERROR`, storage instability                                         |

{% hint style="info" %}
**Acceptance ≠ minting.** A `201` only means the request was accepted and queued (`status: "waiting_for_mining"`). Blockchain-level failures (e.g. funds, nonce, gas) happen **after** acceptance and are reported via the [webhook](/issue-transfer-api/issue-and-transfer-srr-nft/webhook-setup), not in the HTTP response. See [Asynchronous (post-acceptance) failures](#asynchronous-post-acceptance-failures).
{% endhint %}

## Errors by status

{% tabs %}
{% tab title="401" %}
Returned by **all** Commerce endpoints when the API-key / issuer headers are missing or do not match.

```json
{ "statusCode": 401, "message": "Missing header" }
```

```json
{ "statusCode": 401, "message": "API Key is not valid for 0xA6E6a9E20a541680a1D6E1412f5088AefBF58a22" }
```

| Message                              | Cause                                                      | Fix                                             |
| ------------------------------------ | ---------------------------------------------------------- | ----------------------------------------------- |
| `Missing header`                     | `commerce-api-key` or `issuer-address` header absent/empty | Send both headers on every request              |
| `API Key is not valid for <address>` | The key does not resolve to the supplied `issuer-address`  | Use the API key that belongs to that issuer LUW |
| {% endtab %}                         |                                                            |                                                 |

{% tab title="400" %}
Two families: **request-level** (rejected outright) and **per-SRR** (`message.results[]`).

```json
// Request-level — metadata fails the JSON schema
{
  "statusCode": 400,
  "message": [
    "payload.0.metadata is invalid. Check it against the metadata JSON schema. Details:  should have required property 'title' ({\"missingProperty\":\"title\"}).",
    "payload.0.artistAddress must be a valid Ethereum addresses",
    "payload.0.externalId should not be empty"
  ]
}
```

```json
// Per-SRR — an entry failed an issuance check
{
  "statusCode": 400,
  "message": {
    "results": [
      { "status": "failed", "externalId": "c0456840-...", "message": "DUPLICATE_CHIP" }
    ]
  }
}
```

See [Request-level errors](#request-level-errors-400) and [Per-SRR codes](#per-srr-issuance-error-codes).
{% endtab %}

{% tab title="404" %}
Only from `POST /fileMetadata`, when one or more requested files do not exist in storage. The body lists the missing files:

```json
[
  { "index": 0, "filePath": "0xA6E6.../not-found.jpg" }
]
```

| Cause                                                                                  | Fix                                                                                                                                                                                        |
| -------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Referencing a file that was never uploaded, or whose name/category differs from upload | Upload via [`/signedUrls`](/issue-transfer-api/issue-and-transfer-srr-nft/request-signed-url-for-upload-download-files) first; query with the exact `filename` + `category` used at upload |
| {% endtab %}                                                                           |                                                                                                                                                                                            |

{% tab title="500" %}
A downstream step failed. The message usually carries a `STARTRAIL_ERROR:` prefix wrapping the underlying reason.

```json
{ "statusCode": 500, "message": "STARTRAIL_ERROR: LUW missing NFC tag!" }
```

```json
{ "statusCode": 500, "message": "STARTRAIL_ERROR: Quota limit exceeded!" }
```

| Message (contains)                                              | Cause                                                                                                             | Fix                                                                               |
| --------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------- |
| `STARTRAIL_ERROR: Quota limit exceeded!`                        | The issuer LUW's SRR issuance quota is exhausted                                                                  | Top up the issuance quota for the LUW                                             |
| `STARTRAIL_ERROR: LUW missing NFC tag!`                         | NFC tag UIDs in the request exceed the LUW's NFC-tag allowance — see [NFC tag errors](#nfc-tags-and-quota-errors) | Reduce chips in the request or top up NFC-tag allowance                           |
| `STARTRAIL_ERROR: <other>`                                      | A forwarded Startrail / signing failure                                                                           | Retry; if it persists [contact us](https://startbahn.io/contact) with the message |
| `UNKNOWN_ERROR`                                                 | Unclassified server error                                                                                         | Retry; contact support if persistent                                              |
| `It may happen if the network to bucket storage is not stable.` | Transient storage error on `/signedUrls` or `/fileMetadata`                                                       | Retry the request                                                                 |
| {% endtab %}                                                    |                                                                                                                   |                                                                                   |
| {% endtabs %}                                                   |                                                                                                                   |                                                                                   |

## Per-SRR issuance error codes

These appear in `message.results[*].message` for `POST /srrs` (HTTP `400`). Each is tied to the `externalId` of the failing entry.

| Code                    | Meaning                                                       | How to fix                                          |
| ----------------------- | ------------------------------------------------------------- | --------------------------------------------------- |
| `DUPLICATE_METADATA`    | An SRR with identical metadata already exists for this artist | Change the metadata, or skip — it is already issued |
| `DUPLICATE_CHIP`        | A chip UID in the request is already attached to another SRR  | Remove/replace the duplicated chip UID              |
| `INVALID_CHIP`          | A chip UID is malformed or not accepted                       | Correct the chip UID value(s)                       |
| `DUPLICATE_EXTERNAL_ID` | The `externalId` was already used by a previous issuance      | Use a fresh, unique `externalId`                    |
| `UNKNOWN_ARTIST`        | `artistAddress` is not a known Licensed User Wallet           | Use a valid artist LUW address                      |
| `SRR_NOT_CREATED`       | The SRR could not be created on chain                         | Retry; contact support if it persists               |

{% hint style="info" %}
Because the batch is atomic, a single failed entry fails the whole request. The `results[]` array tells you **which** `externalId` to fix.
{% endhint %}

## Request-level errors (400)

Rejected before issuance begins.

| Message                                                                                                                                                                                                                                                                                                                         | Endpoint(s)                    | Cause                                                                                                                                                                                                                                                                             | Fix                                                                                                                                                              |
| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `<field> is invalid. Check it against the metadata JSON schema. Details: …`                                                                                                                                                                                                                                                     | `/srrs`                        | `metadata` fails the SRR JSON schema                                                                                                                                                                                                                                              | See [Metadata validation](#metadata-validation-errors)                                                                                                           |
| `Some of the metadata is not defined`                                                                                                                                                                                                                                                                                           | `/srrs`                        | A `payload[]` entry has no `metadata`                                                                                                                                                                                                                                             | Provide `metadata` for every entry                                                                                                                               |
| `Some metadata is failed to be converted: <reason>`                                                                                                                                                                                                                                                                             | `/srrs`                        | Metadata could not be upgraded to the current schema                                                                                                                                                                                                                              | Fix the metadata per `<reason>`                                                                                                                                  |
| `Address not found: <addresses>`                                                                                                                                                                                                                                                                                                | `/srrs`                        | `issuer-address` or a `payload[*].artistAddress` is not a known LUW                                                                                                                                                                                                               | Use valid LUW addresses                                                                                                                                          |
| `File URLs must be Startbahn storage URLs (the finalUrl returned by /commerce/signedUrls) or ipfs:// URLs. Upload the files via /commerce/signedUrls and use the returned finalUrl for: [<externalId>] <field>: <url>`                                                                                                          | `/srrs`                        | A `metadata.thumbnailURL`, `metadata.image`, `metadata.contractTerms.fileURL` or `attachmentFiles[*].url` points at an external host — see [File URLs](/issue-transfer-api/issue-and-transfer-srr-nft/issue-and-transfer#file-urls-every-file-must-come-from-the-signed-url-flow) | Upload the file via [`/signedUrls`](/issue-transfer-api/issue-and-transfer-srr-nft/request-signed-url-for-upload-download-files) and use the returned `finalUrl` |
| `Metadata file URLs (thumbnailURL, image, contractTerms.fileURL) must be publicly accessible - files in the private bucket cannot be served or published to IPFS. Upload them via /commerce/signedUrls with a public file category (e.g. non_attachment_file) and use the returned finalUrl for: [<externalId>] <field>: <url>` | `/srrs`                        | A `thumbnailURL` / `image` / `contractTerms.fileURL` points at a file uploaded with a **private** category (`certificate`, `for_authenticity`, `installation`)                                                                                                                    | Re-upload with a public category — `non_attachment_file` (recommended) or `artwork` — and use that `finalUrl`                                                    |
| `Metadata file(s) not found in storage. Upload them via /commerce/signedUrls and use the returned finalUrl for: [<externalId>] <field>: <url>`                                                                                                                                                                                  | `/srrs`                        | A `thumbnailURL` / `image` / `contractTerms.fileURL` references a Startbahn URL whose file was never uploaded                                                                                                                                                                     | Upload first via `/signedUrls`                                                                                                                                   |
| `Metadata file(s) not ready for IPFS publication (CID still being calculated). Check readiness via /commerce/fileMetadata and retry for: [<externalId>] <field>: <url>`                                                                                                                                                         | `/srrs`                        | The referenced file is uploaded but its IPFS CID is still computing                                                                                                                                                                                                               | Poll [`/fileMetadata`](/issue-transfer-api/issue-and-transfer-srr-nft/file-information-metadata) until `cid` is present, then retry                              |
| `One or more attachment files were not found in storage. Upload them via /commerce/signedUrls before issuing.`                                                                                                                                                                                                                  | `/srrs`                        | An `attachmentFiles[*].url` is not an uploaded file                                                                                                                                                                                                                               | Upload first via `/signedUrls`                                                                                                                                   |
| `Attachment hash not ready for <url>. Wait for hash calculation and retry, or check via /commerce/fileMetadata.`                                                                                                                                                                                                                | `/srrs`                        | The file is uploaded but its hash is still computing                                                                                                                                                                                                                              | Poll [`/fileMetadata`](/issue-transfer-api/issue-and-transfer-srr-nft/file-information-metadata) until `hash` is present, then retry                             |
| `Duplicate Request Error: Same requestId already logged`                                                                                                                                                                                                                                                                        | `/srrs`                        | The `requestId` was already used for this issuer                                                                                                                                                                                                                                  | Use a new unique `requestId` (UUID)                                                                                                                              |
| `Request Content Error: Current request already logged with status <in_progress\|successful>`                                                                                                                                                                                                                                   | `/srrs`                        | An identical payload is already in flight or already succeeded                                                                                                                                                                                                                    | Do not resubmit; read the result via the [subgraph](/subgraph/subgraph)                                                                                          |
| `extension is missing` / `space is not allowed`                                                                                                                                                                                                                                                                                 | `/signedUrls`, `/fileMetadata` | `filename` has no extension or contains a space                                                                                                                                                                                                                                   | Use a filename like `cert.pdf` with no spaces                                                                                                                    |

{% hint style="success" %}
**Idempotency.** Send a unique `requestId` (UUID) on every `POST /srrs`. A retried request with the same `requestId` is safely rejected instead of double-issuing.
{% endhint %}

## NFC tags and quota errors

If you attach NFC/IC tags to SRRs, two allowance checks apply to the issuer LUW.

{% hint style="danger" %}
**`STARTRAIL_ERROR: LUW missing NFC tag!`** does **not** mean a tag is unregistered. It means the **number of IC tag UIDs in your request exceeds the LUW's available NFC-tag allowance**.
{% endhint %}

| Error                   | Rule                                                 | Notes                                                                                                                   |
| ----------------------- | ---------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- |
| `Quota limit exceeded!` | SRR issuance count exceeds the LUW's quota           | Counts SRRs being issued                                                                                                |
| `LUW missing NFC tag!`  | IC tag UID count exceeds the LUW's NFC-tag allowance | **Single request:** that entry's tag count. **Bulk request:** the **sum** of tag UIDs across all entries in the request |

* You do **not** pre-register individual tag UIDs — they are registered via `metadata` at issuance. You only need enough NFC-tag allowance on the LUW beforehand.
* In schema **v2.2**, the IC tag UIDs go in `chipUIDs`, and `startbahnCertICTagUIDs` must be supplied **with the same values** (the two are mutually required). See the recipe and schema:

{% content-ref url="/pages/0jhQ5HkfFA9BLusH5SmK" %}
[Request Payloads & Recipes](/issue-transfer-api/issue-and-transfer-srr-nft/payloads)
{% endcontent-ref %}

{% content-ref url="/pages/cfyaLly9LtHuauzFV359" %}
[Version 2.2](/metadata-schema/startrail-registry-srr/version-2.2)
{% endcontent-ref %}

## Metadata validation errors

`metadata` is validated against the [SRR JSON schema](/metadata-schema/startrail-registry-srr) (version 2.0+). Failures return `400` with a message of the form:

```
payload.<i>.metadata is invalid. Check it against the metadata JSON schema. Details: <detail>. <detail>. …
```

Common `<detail>` strings:

| Detail                                                                 | Cause                                                                                                         | Fix                                                        |
| ---------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------- |
| `should have required property '<prop>'`                               | A required field is missing (`$schema`, `name`, `title`, `thumbnailURL`, `image`, `medium`, `yearOfCreation`) | Add the field                                              |
| `should NOT have additional properties ({"additionalProperty":"<x>"})` | A field not allowed by the schema version was sent (`additionalProperties: false`)                            | Remove `<x>`, or use a schema version that defines it      |
| `must be equal to one of the allowed values`                           | An `enum` value (e.g. `size.unit`, `edition.proofType`) is invalid                                            | Use an allowed value                                       |
| `$schema path is invalid, file is not found`                           | `$schema` points at an unknown schema version                                                                 | Use a valid `$schema` URL                                  |
| `$schemaIntegrity does not match $schema digest`                       | `$schemaIntegrity` hash is wrong                                                                              | Remove it (not required in v2.2) or set the correct digest |
| `royalty rate is out of the acceptable range of 0 to 100`              | `contractTerms.royaltyRate` out of range                                                                      | Use 0–100                                                  |

{% hint style="warning" %}
**v2.2 gotcha:** `chipUIDs` and `startbahnCertICTagUIDs` are **mutually required** — supplying only one fails with `should have required property 'chipUIDs'` (or `startbahnCertICTagUIDs`). And if `isDigital: true`, `digitalComponents` becomes required.
{% endhint %}

## Asynchronous (post-acceptance) failures

A `201` response (`status: "waiting_for_mining"`) means the request was accepted, not that minting succeeded. Blockchain-send failures surface later via the [webhook](/issue-transfer-api/issue-and-transfer-srr-nft/webhook-setup). Underlying reasons you may see in support/logs:

| Reason                                                | Meaning                                                                      |
| ----------------------------------------------------- | ---------------------------------------------------------------------------- |
| `API account is out of funds`                         | The relayer account needs funding — the transaction is retried automatically |
| `nonce is out of sync`                                | Transient nonce issue — auto-resent                                          |
| `transaction underpriced` / `replacement underpriced` | Gas price too low — auto-resent                                              |
| `gas required exceeds allowance`                      | Transaction would exceed gas limits                                          |

Confirm final state via the [subgraph](/subgraph/subgraph) or the issue/transfer [webhook](/issue-transfer-api/issue-and-transfer-srr-nft/webhook-setup).

## How to decode an error — quick steps

1. **Check `statusCode`** → use the [overview table](#status-code-overview).
2. **Is `message` an object with `results[]`?** → it's a [per-SRR code](#per-srr-issuance-error-codes); the failing `externalId` is named.
3. **Is `message` a string/array?** → it's a [request-level error](#request-level-errors-400); fix and resubmit the whole batch.
4. **`STARTRAIL_ERROR:` prefix?** → a downstream issue; check for [NFC/quota](#nfc-tags-and-quota-errors) keywords, else retry.
5. **Got `201` but no SRR minted?** → watch the [webhook](/issue-transfer-api/issue-and-transfer-srr-nft/webhook-setup); see [async failures](#asynchronous-post-acceptance-failures).

## Quick reference (for tooling/LLMs)

| key                                                  | status | shape     | meaning                                                                  |
| ---------------------------------------------------- | ------ | --------- | ------------------------------------------------------------------------ |
| `Missing header`                                     | 401    | string    | auth header absent                                                       |
| `API Key is not valid for <addr>`                    | 401    | string    | key ≠ issuer                                                             |
| `DUPLICATE_METADATA`                                 | 400    | per-SRR   | metadata already issued for artist                                       |
| `DUPLICATE_CHIP`                                     | 400    | per-SRR   | chip UID already used                                                    |
| `INVALID_CHIP`                                       | 400    | per-SRR   | chip UID malformed                                                       |
| `DUPLICATE_EXTERNAL_ID`                              | 400    | per-SRR   | externalId reused                                                        |
| `UNKNOWN_ARTIST`                                     | 400    | per-SRR   | artistAddress not a LUW                                                  |
| `SRR_NOT_CREATED`                                    | 400    | per-SRR   | on-chain create failed                                                   |
| `… metadata is invalid …`                            | 400    | string\[] | JSON-schema failure                                                      |
| `Some of the metadata is not defined`                | 400    | string    | entry missing metadata                                                   |
| `Address not found: <addrs>`                         | 400    | string    | issuer/artist not a LUW                                                  |
| `Duplicate Request Error: …`                         | 400    | string    | requestId reused                                                         |
| `Request Content Error: …`                           | 400    | string    | identical payload in flight/done                                         |
| `Attachment hash not ready …`                        | 400    | string    | file hash still computing                                                |
| `… attachment files were not found …`                | 400    | string    | file not uploaded                                                        |
| `File URLs must be Startbahn storage URLs …`         | 400    | string    | external URL in thumbnailURL/image/contractTerms.fileURL/attachmentFiles |
| `Metadata file URLs … must be publicly accessible …` | 400    | string    | thumbnailURL/image/contractTerms.fileURL in the private bucket           |
| `Metadata file(s) not found in storage …`            | 400    | string    | metadata file URL never uploaded                                         |
| `Metadata file(s) not ready for IPFS publication …`  | 400    | string    | file CID still computing                                                 |
| `STARTRAIL_ERROR: Quota limit exceeded!`             | 500    | string    | SRR quota exhausted                                                      |
| `STARTRAIL_ERROR: LUW missing NFC tag!`              | 500    | string    | NFC-tag allowance exceeded                                               |
| `STARTRAIL_ERROR: <msg>`                             | 500    | string    | forwarded downstream failure                                             |
| `UNKNOWN_ERROR`                                      | 500    | string    | unclassified                                                             |
| `[{ index, filePath }]`                              | 404    | object\[] | files not in storage (`/fileMetadata`)                                   |


# Webhook Setup

## Overview

Startbahn delivers integration events from your Licensed User Wallet (LUW) to an HTTPS endpoint you control. Each event is sent as a JSON `POST` request and is retried on failure.

You can use webhooks to react to on-chain activity in near real-time — for example, persisting newly issued SRRs, advancing a transfer flow once the on-chain reservation is confirmed, or updating internal state when a collection contract finishes deploying.

{% hint style="info" %}
Webhook subscriptions are not self-service today. The Startbahn team will configure your endpoint, API key, and the set of events you want to receive based on the [Subscribing to webhooks](#subscribing-to-webhooks) information you provide.
{% endhint %}

## Events at a glance

| Subscription name             | Payload `type`             | Fires when                                                 |
| ----------------------------- | -------------------------- | ---------------------------------------------------------- |
| `issueComplete`               | `issue_complete`           | One or more SRRs have been minted.                         |
| `transferReservationComplete` | `transfer_key`             | A transfer has been reserved on-chain (transfer key flow). |
| `transferExecutionComplete`   | `transfer_complete`        | A transfer has executed on-chain and the owner changed.    |
| `collectionCreated`           | `collection_created`       | A collection contract has been deployed.                   |
| `collectionCreateFailed`      | `collection_create_failed` | A collection-creation transaction failed to be mined.      |

## Delivery model

### Request format

Each delivery is an HTTP `POST` to your configured URL with a JSON body and the following headers:

```
Content-Type: application/json
x-api-key: <the API key you provided when subscribing>
```

Use the `x-api-key` header to authenticate the request on your side. Treat the key as a shared secret.

### Expected response

Respond with any `2xx` status to acknowledge receipt. Your endpoint should respond quickly; long-running work should be queued on your side and processed asynchronously.

### Retries and cancellation

If your endpoint returns a non-`2xx` status, times out, or is unreachable, the delivery is retried. You can configure the total number of attempts (between `1` and `5`, default `1`).

Once every attempt fails, Startbahn:

1. Marks the delivery as cancelled and stops retrying it.
2. Sends an informational email (in CSV format) to the contact email you provided, containing the same data that the webhook would have delivered.

Cancellations are per-delivery — subsequent events are still attempted independently.

### Batching and `groupId`

When multiple events of the same `type` are destined for the same URL and API key, they may be combined into a single delivery whose `data` array contains all of them.

Each entry in `data` carries a `groupId` so you can correlate or deduplicate entries:

* For SRR-related events, `groupId` is the SRR's token ID.
* For collection-related events, `groupId` is `${ownerAddress}${name}` — i.e. the LUW address concatenated with the collection name.

## Common payload structure

Every webhook body shares the same envelope:

```json
{
  "type": "<event payload type>",
  "version": <integer>,
  "data": [ /* one or more entries */ ]
}
```

<mark style="color:red;">`*`</mark> indicates the field is always present.

<table><thead><tr><th width="240">Field</th><th width="140">Format</th><th>Description</th></tr></thead><tbody><tr><td><code>type</code><mark style="color:red;">*</mark></td><td>enum</td><td>One of <code>issue_complete</code>, <code>transfer_key</code>, <code>transfer_complete</code>, <code>collection_created</code>, <code>collection_create_failed</code>.</td></tr><tr><td><code>version</code><mark style="color:red;">*</mark></td><td>integer</td><td>Payload schema version. SRR events are currently <code>2</code>; collection events are currently <code>1</code>.</td></tr><tr><td><code>data</code><mark style="color:red;">*</mark></td><td>array</td><td>One or more entries. The shape of each entry depends on <code>type</code>.</td></tr><tr><td><code>data[*].groupId</code><mark style="color:red;">*</mark></td><td>string</td><td>Identifier used to group related entries inside a single delivery. See <a href="#batching-and-groupid">Batching and <code>groupId</code></a>.</td></tr></tbody></table>

{% hint style="warning" %}
**Forward compatibility:** We may add new fields to a payload without bumping `version`. Make sure your parser ignores unknown fields rather than rejecting them. Removals will be announced and deprecated first.
{% endhint %}

## SRR webhook events

The three SRR events (`issueComplete`, `transferReservationComplete`, `transferExecutionComplete`) share a common set of fields in addition to the envelope above.

### Fields common to all SRR events

<table><thead><tr><th width="260">Field</th><th width="140">Format</th><th>Description</th></tr></thead><tbody><tr><td><code>data[*].srrId</code><mark style="color:red;">*</mark></td><td>string</td><td>The SRR's token ID.</td></tr><tr><td><code>data[*].collectionContractAddress</code><mark style="color:red;">*</mark></td><td>string | null</td><td>Address of the collection contract the SRR lives on. <code>null</code> means the SRR is on the default collection. Added in payload <code>version: 2</code>.</td></tr><tr><td><code>data[*].metadata</code></td><td>object</td><td>Raw SRR metadata JSON. See <a href="/pages/hx1j9Oc1YPYQy3C93RIm">Metadata Schema</a> for the shape.</td></tr></tbody></table>

{% hint style="info" %}
**`srrId` is not globally unique** — two SRRs on two different collections can share the same token ID. Treat `(collectionContractAddress, srrId)` as the unique identifier when persisting or looking up SRRs on your side.
{% endhint %}

{% hint style="warning" %}
**`data[*].metadata` shape**

`data[*].metadata` is the **raw SRR metadata JSON** — the same object you can fetch from the [Startrail IPFS CDN gateway](/subgraph/ipfs-cdn-gateway) at `https://cdn.startrail.io/ipfs/<cid>`. It is not wrapped in a `{ digest, json, createdAt, updatedAt, cid }` envelope.

The CID is conveyed separately:

* On `issueComplete`, via `data[*].srrCid` (when available).
* On `transferReservationComplete` / `transferExecutionComplete`, the CID is not on the payload — look the SRR up on the [subgraph](/subgraph/subgraph) by `srrId` to get the current `metadataDigest`.
  {% endhint %}

### `issueComplete`

Fires after one or more SRRs have been minted by the subscribed issuer.

**Event-specific fields**

<table><thead><tr><th width="260">Field</th><th width="140">Format</th><th>Description</th></tr></thead><tbody><tr><td><code>data[*].srrCid</code></td><td>string</td><td>CID of the SRR metadata on IPFS. Provided when the issuance flow knows the CID. See <a href="https://docs.ipfs.tech/concepts/content-addressing/#what-is-a-cid">CID documentation</a>.</td></tr></tbody></table>

{% tabs %}
{% tab title="Example payload" %}

```json
{
  "type": "issue_complete",
  "version": 2,
  "data": [
    {
      "groupId": "123456789012",
      "srrId": "123456789012",
      "collectionContractAddress": null,
      "srrCid": "bafkreid6i2u5b26hepprrkcswqoknzpyl2mrvuoy2ewuktj6ye5bxl3mby",
      "metadata": {
        "$schema": "https://api.startrail.io/api/v1/schema/registry-record-metadata.v2.1.schema.json",
        "$schemaIntegrity": "sha256-fff288406b907ee6472585388bf519573628e45592be368f128b5b1e37a947c9",
        "startbahnCertICTagUIDs": ["1234567890abcdef"],
        "title": {
          "en": "A title",
          "ja": "タイトル",
          "zh": "一个标题"
        },
        "size": {
          "width": 200,
          "height": 400,
          "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": "https://startrail.io/whitepaper/startrail_wp_en_v1.1.pdf"
        },
        "note": { "en": "note", "zh": "注意" },
        "thumbnailURL": "https://cdn.startrail.io/ipfs/bafkreigb2wjgin53xgmaiqxvdn4g2iw6cnmp4e3w3nzopmom53sjborque",
        "yearOfCreation": {
          "en": "around 2010-2020",
          "ja": "2010年から2020年頃"
        },
        "isDigital": true,
        "digitalDataHash": "sha256-247e4b904322a1dd0b148cd77e8627ec7d391251380880ab4621726ecb945ef5"
      }
    }
  ]
}
```

{% endtab %}
{% endtabs %}

### `transferReservationComplete`

Fires after a transfer reservation has been confirmed on-chain (the transfer-key flow). The subscribed previous owner receives the notification.

**Event-specific fields**

No fields beyond the [common SRR fields](#fields-common-to-all-srr-events).

{% hint style="info" %}
The legacy `transferCid`, `dataUrl`, and `encryptedTransferKey` fields are no longer emitted on this webhook. If you need the encrypted transfer key, fetch it through the separate transfer-key API.
{% endhint %}

{% tabs %}
{% tab title="Example payload" %}

```json
{
  "type": "transfer_key",
  "version": 2,
  "data": [
    {
      "groupId": "123456789012",
      "srrId": "123456789012",
      "collectionContractAddress": null,
      "metadata": {
        "$schema": "https://api.startrail.io/api/v1/schema/registry-record-metadata.v2.1.schema.json",
        "title": { "en": "A title" }
        // ... full SRR metadata JSON, same shape as on the IPFS CDN gateway
      }
    }
  ]
}
```

{% endtab %}
{% endtabs %}

### `transferExecutionComplete`

Fires after a transfer has executed on-chain and the SRR's owner has changed. The subscribed previous owner receives the notification.

**Event-specific fields**

<table><thead><tr><th width="260">Field</th><th width="140">Format</th><th>Description</th></tr></thead><tbody><tr><td><code>data[*].newOwnerEoa</code><mark style="color:red;">*</mark></td><td>string</td><td>EOA address of the SRR's new owner.</td></tr></tbody></table>

{% hint style="info" %}
The legacy `transferCid` field is no longer emitted on this webhook. If you need the transfer's provenance entry, query it from the [subgraph](/subgraph/subgraph) by `srrId`.
{% endhint %}

{% tabs %}
{% tab title="Example payload" %}

```json
{
  "type": "transfer_complete",
  "version": 2,
  "data": [
    {
      "groupId": "123456789012",
      "srrId": "123456789012",
      "collectionContractAddress": null,
      "newOwnerEoa": "0x887C0d2340d2Fa144289C2E2BF835556f5c6C4E0",
      "metadata": {
        "$schema": "https://api.startrail.io/api/v1/schema/registry-record-metadata.v2.1.schema.json",
        "title": { "en": "A title" }
        // ... full SRR metadata JSON, same shape as on the IPFS CDN gateway
      }
    }
  ]
}
```

{% endtab %}
{% endtabs %}

## Collection webhook events

Collection events relate to the lifecycle of a collection contract owned by your LUW. The two events share a common set of fields in addition to the envelope.

### Fields common to all collection events

<table><thead><tr><th width="260">Field</th><th width="140">Format</th><th>Description</th></tr></thead><tbody><tr><td><code>data[*].name</code><mark style="color:red;">*</mark></td><td>string</td><td>Name of the collection.</td></tr><tr><td><code>data[*].symbol</code><mark style="color:red;">*</mark></td><td>string</td><td>Symbol of the collection.</td></tr></tbody></table>

For collection events, `groupId` is composed as `${ownerAddress}${name}` and matches across the success and failure events for the same attempt.

### `collectionCreated`

Fires after a collection contract has been deployed.

**Event-specific fields**

<table><thead><tr><th width="260">Field</th><th width="140">Format</th><th>Description</th></tr></thead><tbody><tr><td><code>data[*].contractAddress</code><mark style="color:red;">*</mark></td><td>string</td><td>Address of the newly deployed collection contract.</td></tr><tr><td><code>data[*].ownerAddress</code><mark style="color:red;">*</mark></td><td>string</td><td>LUW address that owns the collection.</td></tr></tbody></table>

{% tabs %}
{% tab title="Example payload" %}

```json
{
  "type": "collection_created",
  "version": 1,
  "data": [
    {
      "groupId": "0x9f25c0d8eB5f461528ab5E02f1F31C77885d5Dc0collection name",
      "contractAddress": "0x229dbFE303C5706BDB570A42f0BA190621d2D032",
      "ownerAddress": "0x9f25c0d8eB5f461528ab5E02f1F31C77885d5Dc0",
      "name": "collection name",
      "symbol": "TT"
    }
  ]
}
```

{% endtab %}
{% endtabs %}

### `collectionCreateFailed`

Fires when a collection-creation transaction fails to be mined.

**Event-specific fields**

No fields beyond the [common collection fields](#fields-common-to-all-collection-events). The owner address can be recovered from the `groupId` (which encodes `${ownerAddress}${name}`) or correlated with the originating request on your side.

{% tabs %}
{% tab title="Example payload" %}

```json
{
  "type": "collection_create_failed",
  "version": 1,
  "data": [
    {
      "groupId": "0x9f25c0d8eB5f461528ab5E02f1F31C77885d5Dc0collection name",
      "name": "collection name",
      "symbol": "TT"
    }
  ]
}
```

{% endtab %}
{% endtabs %}

## Subscribing to webhooks

To enable webhook delivery, provide Startbahn with the following information.

<mark style="color:red;">`*`</mark> indicates the field is required.

<table><thead><tr><th width="220">Field</th><th width="320">Description</th><th>Format / Example</th></tr></thead><tbody><tr><td>Webhook URL<mark style="color:red;">*</mark></td><td>The HTTPS endpoint where Startbahn will POST events.</td><td><code>https://www.your-company.com/srr-integration-webhooks/</code></td></tr><tr><td>API Key<mark style="color:red;">*</mark></td><td>Shared secret that Startbahn will send in the <code>x-api-key</code> header. Use this on your side to authenticate the request. This key should be different from your Issue API key.</td><td>Any string matching <code>^[a-zA-Z0-9_+-]{30,100}$</code><br>Example: <code>e46b1263-e3f5-461d-bfe7-18aff21c5ed3</code></td></tr><tr><td>Public key</td><td>RSA public key used to encrypt the transfer key when using the transfer-key integration. You hold the private key and use it to decrypt. Only required when subscribing to <code>transferReservationComplete</code> with the transfer-key flow.</td><td>JSON object describing the key.<br>Example: <code>{"alg":"RSA-OAEP-256","e":"AQAB","ext":true,"key_ops":["encrypt"],"kty":"RSA","n":"...","..."}</code><br><br>You can generate a key pair at <a href="https://codesandbox.io/s/subtlecrypto-rsa-ttql3">this SubtleCrypto example</a> or with any tool using equivalent parameters.</td></tr><tr><td>Number of tries</td><td>Total delivery attempts before the event is cancelled, integer <code>1 ≤ n ≤ 5</code>. Defaults to <code>1</code> if omitted.</td><td>Integer.<br><code>1</code> = single attempt, no retries.<br><code>2</code> = single attempt + 1 retry.</td></tr><tr><td>Contact email</td><td>Email address that receives a CSV with the event data when all delivery attempts have failed.</td><td><code>admin@webhook-client.com</code></td></tr><tr><td>Webhook events to subscribe to<mark style="color:red;">*</mark></td><td>The subscription names you want to receive. You can change the subscription set at any time for future events.</td><td>List of subscription names from the <a href="#events-at-a-glance">events table</a>.</td></tr></tbody></table>


# Collection

## Overview

Issuing SRRs under collection is similar to issuing SRRs without collection.

When issuing SRRs under collection, we add a few extra processes.

1. Create collection
   1. The Client will receive the collection address when they receive a reply about creating a collection.
   2. The client must ensure that the collection is created. This can be done in one of two ways.
      1. Check the LUW collection.
      2. Get webhook when collection creation is complete. In this way, the client must tell Startbahn to subscribe to the collection\_creation webhook.
2. Issue SRR
   1. Include the collection address in the request of issuance.

## Endpoints Used

* [Create Collection](/issue-transfer-api/collection/create-collection)
* [Get Collection of LUW](/issue-transfer-api/collection/get-collection-of-luw)
* [Issue & Transfer](/issue-transfer-api/issue-and-transfer-srr-nft/issue-and-transfer)
* [Webhook Setup](/issue-transfer-api/issue-and-transfer-srr-nft/webhook-setup)

## Required Permissions

Check [permission at Issue & Transfer](/issue-transfer-api/issue-and-transfer-srr-nft#required-permissions)

## Required Headers

Check [headers at Issue & Transfer](/issue-transfer-api/issue-and-transfer-srr-nft#required-headers)


# Create Collection

to create a new collection

<mark style="color:green;">`POST`</mark> `<base_url>/port/api/v1/commerce/collection`

Please replace `<base_url>` as explained [here](/readme/url-per-environment).

#### Headers

| Name                                               | Type   | Description                       |
| -------------------------------------------------- | ------ | --------------------------------- |
| commerce-api-key<mark style="color:red;">\*</mark> | string | Commerce API Key                  |
| issuer-address<mark style="color:red;">\*</mark>   | string | Contract Address of API Key owner |

#### Request Body

| Name                                             | Type   | Description                                                                                                                                                                                                                                          |
| ------------------------------------------------ | ------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| payload<mark style="color:red;">\*</mark>        | object | Single object of collection detail that will be created.                                                                                                                                                                                             |
| payload.name<mark style="color:red;">\*</mark>   | string | Name of the collection                                                                                                                                                                                                                               |
| payload.symbol<mark style="color:red;">\*</mark> | string | Collection symbol based on [ERC721Metadata symbol](https://docs.openzeppelin.com/contracts/4.x/api/token/erc721#IERC721Metadata-symbol--).                                                                                                           |
| payload.salt                                     | string | <p>Salt used in Collection creation.</p><p>If provided: it must be a random keccak256 hash like <code>0xea9369d265ddf31c12231b2aeb90662018499cb62117f30cf722bc1b76c62c46</code></p><p>otherwise, if not provided: it will be randomly generated.</p> |

{% tabs %}
{% tab title="201: Created " %}
The API responds with 201 when the collection transaction is sent to the blockchain network.

{% tabs %}
{% tab title="Body" %}

<table><thead><tr><th width="238">Body Attribute</th><th>Description</th><th>Format</th></tr></thead><tbody><tr><td>result</td><td>Single object of collection creation result</td><td>object</td></tr><tr><td>result.name</td><td>The value will be the same as the parameter sent in the request</td><td>string</td></tr><tr><td>result.symbol</td><td>The value will be the same as the parameter sent in the request.</td><td>string</td></tr><tr><td>result.collectionAddress</td><td>collection address that is generated by create2, like <code>0x4412Ba95BEC0CDB4562D97CB9149575Ea5B514A5</code></td><td>string</td></tr><tr><td>result.status</td><td><p>The status of the collection creation transaction.</p><ul><li><code>waiting_for_mining</code> : Mining is in progress.</li><li><code>failed</code>: Collection creation transaction is failed.</li></ul></td><td>string</td></tr></tbody></table>
{% endtab %}

{% tab title="Example" %}

```json
{
  "result": {
    "name": "some nft collection name",
    "symbol": "T22",
    "collectionAddress": "0x4412Ba95BEC0CDB4562D97CB9149575Ea5B514A5",
    "status": "waiting_for_mining"
  }
}
```

{% endtab %}
{% endtabs %}
{% endtab %}

{% tab title="400: Bad Request " %}
The API responds with 400 if the request body is invalid.

```
// If payload is incorrect
{
  "statusCode": 400,
  "message": [
    "payload.name should not be null or undefined"
  ]
}
```

{% endtab %}

{% tab title="500: Internal Server Error " %}
The API responds with 500 if there is an issue between the network.

```
// If the network is error or unknown error. Startbahn side need to check.
{
  "statusCode": 502,
  "message": "Bad Gateway"
}
```

{% endtab %}
{% endtabs %}

## Request Body Example

```json
{
  "payload": {
    "name": "some nft collection name",
    "symbol": "T22",
    "salt": "0xea9369d265ddf31c12231b2aeb90662018499cb62117f30cf722bc1b76c62c46"
  }
}
```


# Get Collection of LUW

<mark style="color:blue;">`GET`</mark> `<base_url>/startrail/api/v1/licensedUser/{luw-address}/collections`

Please replace `<base_url>` as explained [here](/readme/url-per-environment).

#### Path Parameters

| Name                                          | Type   | Description        |
| --------------------------------------------- | ------ | ------------------ |
| luw-address<mark style="color:red;">\*</mark> | string | The address of LUW |

#### Headers

| Name         | Type   | Description      |
| ------------ | ------ | ---------------- |
| Content-Type | string | application/json |
| accept       | string | application/json |

{% tabs %}
{% tab title="200: OK " %}
The API responds with 200 if the LUW address is found.

<mark style="color:red;">\*</mark> indicates that the field always exists.

{% tabs %}
{% tab title="Body" %}

| Body Attribute                                          | Description                                                                                      | Format          |
| ------------------------------------------------------- | ------------------------------------------------------------------------------------------------ | --------------- |
|                                                         | Array of object that defines collection                                                          | Array of object |
| \[\*].name<mark style="color:red;">\*</mark>            | Name of the collection. Stored on the contract                                                   | string          |
| \[\*].symbol<mark style="color:red;">\*</mark>          | Symbol to identify the collection                                                                | string          |
| \[\*].contractAddress<mark style="color:red;">\*</mark> | Contract address of the collection contract. Will be visible on NFT marketplaces such as OpenSea | string          |
| \[\*].ownerAddress<mark style="color:red;">\*</mark>    | Address of the owner of the collection                                                           | string          |
| \[\*].createdAt<mark style="color:red;">\*</mark>       | Datetime of the collection creation                                                              | Date            |
| \[\*].updatedAt<mark style="color:red;">\*</mark>       | Datetime of the last update of collection                                                        | Date            |
| {% endtab %}                                            |                                                                                                  |                 |

{% tab title="Example" %}

```json
[
  {
    "name": "Super Cool Tokens",
    "symbol": "SCT",
    "contractAddress": "0x87Ef5da2c87e047E7F005Efb8b68a93Dc94D161c",
    "ownerAddress": "0x40b29c5fe4427f0C09Dd595B983a21322fe6A101",
    "createdAt": "2023-03-24T03:20:31.877Z",
    "updatedAt": "2023-03-24T03:20:31.877Z"
  }
]
```

{% endtab %}
{% endtabs %}
{% endtab %}

{% tab title="404: Not Found " %}
The API responds with 404 if the LUW address is not found.

```json
{
  "statusCode": 404,
  "message": "Cannot GET /api/v1/licensedUser/not-found-luw-address/collections"
}
```

{% endtab %}

{% tab title="500: Internal Server Error " %}
The API responds with 500 if there is an issue between the network.

```json
// If the network is error or unknown error. Startbahn side need to check.
{
  "statusCode": 502,
  "message": "Bad Gateway"
}
```

{% endtab %}
{% endtabs %}


# Change Logs

Summary of the changes, updates, and fixes made to the Issue & Transfer API


# v1.3.0

@May 12, 2026

Substantial cleanup of the public Issue & Transfer / Get SRR endpoints. The endpoint paths and request bodies are unchanged; the responses and webhook payloads are slimmer.

## Issue & Transfer SRR — `POST /port/api/v1/commerce/srrs`

* **Response slimmed.** `results[*].srr` now contains only `tokenId`, `metadataCID`, `metadataURL` and `collectionContractAddress`.
* **`metadataURL` added.** Convenience HTTPS URL that resolves the SRR metadata via the [Startrail IPFS CDN gateway](/subgraph/ipfs-cdn-gateway) (`https://cdn.startrail.io/ipfs/<cid>`).
* **Removed from response:** `srr.collection` (object), `srr.artist`, `srr.issuer`, `srr.isPrimaryIssuer`, `srr.issuedAt`, `srr.metadata.json`, `srr.metadata.originalJson`, `srr.metadata.digest`, `srr.createdAt`, `srr.updatedAt`. Get this data from the [subgraph](/subgraph/subgraph) and the IPFS CDN gateway after the transaction is mined.
* **Documented the attachment-files behavior.** The API resolves each `attachmentFiles[*].url` to its GCS path, reads the precomputed SHA-256, and folds `{category, hash}` entries into `metadata.digitalComponents` (`artwork`) or `metadata.attachmentFiles` (other categories) before issuance — callers no longer need to populate those metadata sub-fields themselves.
* **Documented the `externalUrls` payload field.**

## Get Owned SRRs — `GET /port/api/v1/ownerAddress/{ownerAddress}/ownedSrrs`

* Now returns the **subgraph-aggregated SRR shape** (`AggregatedSRR[]`). New fields include `id`, `metadataDigest`, `transferCommitment`, `lockExternalTransfer`, `royaltyReceiver`, `royaltyBasisPoints`, `metadataHistory[]`, `history[]`, `provenance[]`, `transfers[]`. `issuer` / `artist` use `walletAddress` (not `contractAddress`) and gain `salt`, `owners`, `threshold`. `collection` gains `id` and `ownerAddress`.
* The `srrOwnable` wrapper, `status`, `issuedAt`, and the `metadata.{digest,json,createdAt,updatedAt,cid}` envelope are gone. `metadata` is now the raw on-chain JSON.
* **Query params:** `offset` is no longer honored. Use `page` + `limit` only.

## Get SRR by Collection + Token Id — `GET /port/api/v1/collection/{collectionContractAddress}/srr/{tokenId}`

* Now returns the **subgraph `BasicSRR` shape** (no aggregated `metadata` JSON, no `customHistories`, no `transfers`).
* Fetch the `metadata` JSON yourself from `https://cdn.startrail.io/ipfs/<metadataDigest>`, or query the subgraph for richer relations.

## Webhooks

* **`data[*].metadata` shape change** (all SRR-related events): `metadata` is now the raw SRR metadata JSON, no longer wrapped in `{ digest, json, createdAt, updatedAt, cid }`.
* **`transferExecutionComplete`** no longer includes `transferCid`. Look up the transfer's provenance entry on the [subgraph](/subgraph/subgraph) by `srrId` if needed.
* **`transferReservationComplete`** payload contains only the generic SRR fields (`groupId`, `srrId`, `metadata`). The legacy `dataUrl`, `encryptedTransferKey` and `transferCid` fields had already been removed in an earlier release; the docs are now updated to reflect that.

## Recommended new pages

* [Startrail IPFS CDN gateway](/subgraph/ipfs-cdn-gateway) — the recommended gateway for resolving any IPFS CID surfaced by the platform (metadata, images, attachment files).


# v1.2.0

@April 26, 2023

Add collection detail


# v1.1.0

@Oct 18, 2022

* Update the Swagger URL for the Test environment
* Update metadata to v2.1
  * Change the example URL
  * Change the example request body
    * adding `chipUIDs`
  * Add a link to the document explaining the Metadata


# v1.0.1

@Aug 30, 2022

* Update metadata.isDigital to requires at least 1 `artwork` in payload.attachmentFiles
* Add attachment file payload explanation and example.

```json
  "attachmentFiles": [
        {
          "name": "image.jpg",
          "url": "https://bucket-1.example.com/srr-images/image.jpg",
          "category": "artwork"
        },
        {
          "name": "certificate.jpg",
          "url": "https://bucket-2.example.com/srr-images/certificate.jpg",
          "category": "certificate"
        },
        {
          "name": "for_authenticity.jpg",
          "url": "https://bucket-2.example.com/srr-images/for_authenticity.jpg",
          "category": "for_authenticity"
        },
        {
          "name": "installation.jpg",
          "url": "https://bucket-2.example.com/srr-images/installation.jpg",
          "category": "installation"
        }
      ]
```

* Update some field in metadata to be auto-filled.
  * `metadata.name`, If `name` is not designated, it will be auto-filled with `title` value.
  * `metadata.description`, If `description` is not designated, it will be auto-filled with `size`, `medium`, `edition`, `contractTerms.fileURL`.
  * `metadata.image` , If `image` is not designated, it will be auto-filled with `thumbnailUrl` value.


# Startrail Registry (SRR)

Startrail, developed and managed by Startbahn, is a sustainable and scalable blockchain infrastructure that assures the reliability, authenticity, and traceability of artworks (<https://startrail.io/>). SRR stands for Startrail Registry Record, the name of an NFT issued on Startrail. An NFT issued on Startrail permanently records a wealth of data that protects the value of a work, including information on the NFT issuer and a comprehensive history of the work's provenance.

SRR Metadata Schema of supported versions.


# Version 2.2

## External References

These links are helpful to understand what attributes are widely referenced by external applications such as wallets and NFT marketplaces.

* [ERC721 specification](https://eips.ethereum.org/EIPS/eip-721#specification) (You can search by "ERC721 Metadata JSON Schema")
* [OpenSea's metadata standards](https://docs.opensea.io/docs/metadata-standards#metadata-structure)

## Attributes

<mark style="color:red;">`*`</mark> is required.

<table><thead><tr><th width="236">Attribute</th><th width="335">Description</th><th width="204">data format</th><th width="374">Example</th></tr></thead><tbody><tr><td>$schema<mark style="color:red;">*</mark></td><td>IPFS URL to the schema json the given metadata follows. Fixed value for Metadata Version 2.2 in example value.</td><td>URL</td><td>ipfs://bafkreibebzcktpolubbklh73mxkxswkf3nagmvgq3tsnp33xsi6bbye5ay</td></tr><tr><td>chipUIDs</td><td>Array containing the list of the Chip UIDs of a physical artwork</td><td>Array of Strings</td><td>["1234567890abcdef"]</td></tr><tr><td>startbahnCertICTagUIDs</td><td>Define it as same as chipUIDs.</td><td>Array of Strings</td><td>["1234567890abcdef"]</td></tr><tr><td>title<mark style="color:red;">*</mark></td><td>Flexible language description object for title of the work.</td><td>Language Object (see at the bottom of the table)</td><td><p>{</p><p>"en": "A title",</p><p>"ja": "タイトル",</p><p>"zh": "一个标题"</p><p>}</p></td></tr><tr><td>note</td><td>Flexible language note object for title of the work.</td><td>Language Object (see at the bottom of the table)</td><td>{<br>"en": "The material is very fragile",<br>"zh": "该材料非常脆弱"<br>}</td></tr><tr><td>size</td><td>Object describing the size</td><td>object</td><td><p>{ "width": 200.0,</p><p>"height": 400.0,</p><p>"depth": 12.4,</p><p>"unit": "pixel",</p><p>"flexibleDescription":</p><p>{</p><p>"en": "flexibleDescription comes here",</p><p>"ja": "自由だーーー"</p><p>}</p><p>}</p></td></tr><tr><td>size.height</td><td>height dimension number.</td><td>number</td><td>10.5</td></tr><tr><td>size.width</td><td>width dimension number.</td><td>number</td><td>10.5</td></tr><tr><td>size.depth</td><td>depth dimension number.</td><td>number</td><td>10.5</td></tr><tr><td>size.unit</td><td>string specifying the unit of the dimensional numbers.</td><td>enumerated String with one of the following values. ["mm","cm","m","in","ft","pixel"]</td><td>mm</td></tr><tr><td>size.flexibleDescription</td><td>Alternative way to describe the size of the artwork, if the others dont apply.</td><td>Language Object (see at the bottom of the table)</td><td><p>{</p><p>"en": "flexibleDescription comes here",</p><p>"ja": "自由だーーー"</p><p>}</p></td></tr><tr><td>medium<mark style="color:red;">*</mark></td><td>Flexible language description object for the medium.</td><td>Language Object (see at the bottom of the table)</td><td><p>{</p><p>"en": "Oil on canvas",</p><p>"ja": "キャンバスに油彩",</p><p>"zh": "布面油画"</p><p>}</p></td></tr><tr><td>edition</td><td>Edition details</td><td>object</td><td><p>{</p><p>"uniqueness": "unique work",</p><p>"proofType": "ED",</p><p>"number": 1,</p><p>"totalNumber": 3,</p><p>"note": { "en": "some extra notes " }</p><p>}</p></td></tr><tr><td>edition.note</td><td>Note to add details to the Edition information</td><td>Language Object (see at the bottom of the table)</td><td><p>{</p><p>"en": "note comes here",</p><p>"ja": "自由だーーー"</p><p>}</p></td></tr><tr><td>edition.uniqueness</td><td>Uniqueness of artwork</td><td>enumerated String with one of the following values ["unique work", "non unique work", "unknown"]</td><td>unique work</td></tr><tr><td>edition.proofType</td><td><p>Proof type of edition: -ED Edition<br>-AP Artist Proof<br>-TP Trial Proof<br>-SP Special Proof<br>-HC Hors de Commerce<br>-Open Edition</p><p><br>Need to add it when the artwork is edition work.</p></td><td>enumerated String with one of the following values ["ED","AP","TP","SP","HC", "Open Edition"]</td><td>ED</td></tr><tr><td>edition.number</td><td>Edition number (out of total editions)</td><td>number (integer)</td><td>1</td></tr><tr><td>edition.totalNumber</td><td>Total number of editions</td><td>number (integer)</td><td>3</td></tr><tr><td>contractTerms</td><td>Object with contract details</td><td>Object</td><td><p>{</p><p>"royaltyRate": 15.7,</p><p>"fileURL": "https://startrail.io/whitepaper/startrail_wp_en_v1.1.pdf"</p><p>}</p></td></tr><tr><td>contractTerms.royaltyRate</td><td>Royalty rate percentage.</td><td>number (integer)</td><td>15.7</td></tr><tr><td>contractTerms.fileURL</td><td>URL where the contract terms file is stored</td><td>URL</td><td>https://startrail.io/whitepaper/startrail_wp_en_v1.1.pdf</td></tr><tr><td>thumbnailURL<mark style="color:red;">*</mark></td><td>URL where the artwork thumbnail is stored</td><td>URL</td><td><a href="https://storage.googleapis.com/opensea-prod.appspot.com/puffs/3.png">https://storage.googleapis.com/opensea-prod.appspot.com/puffs/3.png</a></td></tr><tr><td>isDigital</td><td>boolean indicator to specify that the work is a digital artwork.</td><td>boolean</td><td>true</td></tr><tr><td>digitalDataHash</td><td><mark style="color:red;">DEPRECATED</mark></td><td>-</td><td>-</td></tr><tr><td>digitalComponents</td><td><p><strong>ISSUE API USER should leave it UNDEFINED. You can specify these values outside metadata.</strong><br></p><p>Array of Digital file objects to specify digital components</p></td><td>Array of Digital File Objects (see at the bottom of the table)</td><td><p>[{</p><p>"hash": "sha256-247e4b904322a1dd0b148cd77e8627ec7d391251380880ab4621726ecb945ef5",</p><p>"category": "artwork"</p><p>}]</p></td></tr><tr><td>attachmentFiles</td><td><p><strong>ISSUE API USER should leave it UNDEFINED. You can specify these values outside metadata.</strong><br></p><p>Array of Digital file objects to specify attachment files.</p></td><td>Array of Digital File Objects (see at the bottom of the table)</td><td>[{ "hash": "sha256-247e4b904322a1dd0b148cd77e8627ec7d391251380880ab4621726ecb945ef5", "category": "certificate" }]</td></tr><tr><td>name<mark style="color:red;">*</mark></td><td>A field defined in ERC721. External interfaces including NFT marketplaces or wallets like OpenSea or MetaMask may show this field's value.<br><br>If <code>name</code> is not defined by client, it will be auto-filled with <code>title.en</code> value.</td><td>string</td><td>An example of title</td></tr><tr><td>description</td><td><p>A field defined in ERC721. External interfaces including NFT marketplaces or wallets like OpenSea or MetaMask may show this field's value.<br></p><p>If description is not designated, it will be auto-filled with other fields such as size, medium, edition, issuer name, artist name, note, yearOfCreation, contractTerms.fileURL.</p></td><td>string</td><td>Description of example NFT</td></tr><tr><td>image<mark style="color:red;">*</mark></td><td><p>A field defined in ERC721. External interfaces including NFT marketplaces or wallets like OpenSea or MetaMask may show this field's value.<br></p><p>If <code>image</code> is not defined by client, it will be auto-filled with thumbnailURL value.</p></td><td>URL</td><td><a href="https://storage.googleapis.com/opensea-prod.appspot.com/puffs/3.png">https://storage.googleapis.com/opensea-prod.appspot.com/puffs/3.png</a></td></tr><tr><td>external_url</td><td><p>External reference URL used for OpenSea.</p><p>if metadata.external_url is designated, it can have different URL from payload.externalUrls . if metadata.external_url is NOT designated, it will have the first URL from payload.externalUrls .</p></td><td>URL</td><td><a href="https://openseacreatures.io/3">https://openseacreatures.io/3</a></td></tr><tr><td>yearOfCreation<mark style="color:red;">*</mark></td><td>Flexible language description object for the year of the creation</td><td>Language Object (see at the bottom of the table)</td><td><p>{</p><p>"en": "around 2010-2020",</p><p>"ja": "2010年から2020年頃"</p><p>}</p></td></tr><tr><td>attributes</td><td>OpenSea customizable filterable attributes. Check <a href="https://docs.opensea.io/docs/metadata-standards#attributes">https://docs.opensea.io/docs/metadata-standards#attributes</a></td><td>Array of Attributes Object (see at the bottom of the table)</td><td><p>[{</p><p>"trait_type": "Mouth",</p><p>"value": "Surprised"</p><p>}]</p></td></tr></tbody></table>

### Object

<table><thead><tr><th width="255">Attribute</th><th width="464">Description</th><th width="204">data format</th><th width="374">Example</th></tr></thead><tbody><tr><td>&#x3C;Language Object></td><td>An object specifying multiple supported languages. The property names are two letter letter language codes from BCP-47, such as en or ja.</td><td>Each property name: 2 char string from BCP-47, value: string</td><td><p>{</p><p>"en": "A title",</p><p>"ja": "タイトル",</p><p>"zh": "一个标题"</p><p>}</p></td></tr><tr><td>&#x3C;Digital File Object></td><td>An object providing details of digital files.</td><td>Object</td><td>{ "hash": "sha256-247e4b904322a1dd0b148cd77e8627ec7d391251380880ab4621726ecb945ef5", "category": "artwork", "URL":"https://some.url.com/file", "name":"filename.jpg" }</td></tr><tr><td>&#x3C;Digital File Object>.hash</td><td><p>ISSUE API USER should leave it UNDEFINED</p><p>sha256 Hash Hex String of the file.</p></td><td>sha256 Hash Hex String</td><td>sha256-f63238ce3b8c4f8a99fb453d716d5451f75508c2e403a58af0412014187e7a61</td></tr><tr><td>&#x3C;Digital File Object>.category</td><td>String describing the category of the file.</td><td>String to describe the category. Supported values in Startrail PORT: ”certificate”, “for_authenticity”,”artwork”,”installation”</td><td>artwork</td></tr><tr><td>&#x3C;Digital File Object>.URL</td><td>url where the file can be accessed</td><td>URL</td><td></td></tr><tr><td>&#x3C;Digital File Object>.name</td><td>name of the file</td><td>string</td><td></td></tr><tr><td>&#x3C;Attributes Object></td><td>OpenSea customizable filterable attributes. Check <a href="https://docs.opensea.io/docs/metadata-standards#attributes">https://docs.opensea.io/docs/metadata-standards#attributes</a></td><td>Array of Object</td><td><p>{</p><p>"trait_type": "Mouth",</p><p>"value": "Surprised"</p><p>}</p></td></tr><tr><td>&#x3C;Attributes Object>.trait_type</td><td>Check <a href="https://docs.opensea.io/docs/metadata-standards#attributes">https://docs.opensea.io/docs/metadata-standards#attributes</a></td><td>string</td><td>Mouth</td></tr><tr><td>&#x3C;Attributes Object>.value</td><td>Check <a href="https://docs.opensea.io/docs/metadata-standards#attributes">https://docs.opensea.io/docs/metadata-standards#attributes</a></td><td>string</td><td>Surprised</td></tr></tbody></table>

### Changes From the Previous Version

1. Change of `$schema`into IPFS URL
2. removal of of `$schemaIntegrity`
3. Addition of a new field `attributes`

## Complete Example

```json
{
  "$schema": "ipfs://bafkreibebzcktpolubbklh73mxkxswkf3nagmvgq3tsnp33xsi6bbye5ay",
  "startbahnCertICTagUIDs": [
    "1234567890abcdef"
  ],
  "chipUIDs": [
    "1234567890abcdef"
  ],
  "title": {
    "en": "A title",
    "ja": "タイトル",
    "zh": "一个标题"
  },
  "size": {
    "width": 200.0,
    "height": 400.0,
    "depth": 12.4,
    "unit": "pixel",
    "flexibleDescription": {
      "en": "flexibleDescription comes here",
      "ja": "自由だーーー"
    }
  },
  "attributes": [
    {
      "trait_type": "Mouth",
      "value": "Surprised"
    }
  ],
  "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": "https://startrail.io/whitepaper/startrail_wp_en_v1.1.pdf"
  },
  "note": {
    "en": "note",
    "zh": "注意"
  },
  "thumbnailURL": "https://storage.googleapis.com/opensea-prod.appspot.com/puffs/3.png",
  "yearOfCreation": {
    "en": "around 2010-2020",
    "ja": "2010年から2020年頃"
  },
  "isDigital": true,
  "digitalDataHash": "sha256-247e4b904322a1dd0b148cd77e8627ec7d391251380880ab4621726ecb945ef5",
  "digitalComponents": [{
    "hash": "sha256-247e4b904322a1dd0b148cd77e8627ec7d391251380880ab4621726ecb945ef5",
    "category": "artwork"
  }],
  "attachmentFiles": [{
    "hash": "sha256-247e4b904322a1dd0b148cd77e8627ec7d391251380880ab4621726ecb945ef5",
    "category": "artwork"
  }],
  "name": "some nft name",
  "description": "some nft description",
  "image": "https://storage.googleapis.com/opensea-prod.appspot.com/puffs/3.png",
  "external_url": "https://openseacreatures.io/3"
}
```


# Version 2.1

## Attributes

<mark style="color:red;">`*`</mark> is required.

<table><thead><tr><th width="255">Attribute</th><th width="464">Description</th><th width="204">data format</th><th width="374">Example</th></tr></thead><tbody><tr><td>$schema<mark style="color:red;">*</mark></td><td>URL to the schema json the given metadata follows. Fixed value for Metadata Version 2.1 in example value.</td><td>URL</td><td><a href="https://api.startrail.io/api/v1/schema/registry-record-metadata.v2.1.schema.json">https://api.startrail.io/api/v1/schema/registry-record-metadata.v2.1.schema.json</a></td></tr><tr><td>$schemaIntegrity<mark style="color:red;">*</mark></td><td>sha256 Hash of the normalized metadata schema. Fixed value for Metadata Version 2.1 in example value.</td><td>SHA Hash Hex String</td><td>sha256-15f8e99eb9d4292287282942db2f2de9bbcc4761c555c6f7da23feec010c1221</td></tr><tr><td>chipUIDs</td><td>Array containing the list of the Chip UIDs of a physical artwork</td><td>Array of Strings</td><td>["1234567890abcdef"]</td></tr><tr><td>startbahnCertICTagUIDs</td><td>Define it as same as chipUIDs. Deprecated.</td><td>Array of Strings</td><td>["1234567890abcdef"]</td></tr><tr><td>title<mark style="color:red;">*</mark></td><td>Flexible language description object for title of the work.</td><td>Language Object (see at the bottom of the table)</td><td><p>{</p><p>"en": "A title",</p><p>"ja": "タイトル",</p><p>"zh": "一个标题"</p><p>}</p></td></tr><tr><td>size</td><td>Object describing the size</td><td>object</td><td><p>{ "width": 200.0,</p><p>"height": 400.0,</p><p>"depth": 12.4,</p><p>"unit": "pixel",</p><p>"flexibleDescription":</p><p>{</p><p>"en": "flexibleDescription comes here",</p><p>"ja": "自由だーーー"</p><p>}</p><p>}</p></td></tr><tr><td>size.height</td><td>height dimension number.</td><td>number</td><td>10.5</td></tr><tr><td>size.width</td><td>width dimension number.</td><td>number</td><td>10.5</td></tr><tr><td>size.depth</td><td>depth dimension number.</td><td>number</td><td>10.5</td></tr><tr><td>size.unit</td><td>string specifying the unit of the dimensional numbers.</td><td>enumerated String with one of the following values. ["mm","cm","m","in","ft","pixel"]</td><td>mm</td></tr><tr><td>size.flexibleDescription</td><td>Alternative way to describe the size of the artwork, if the others dont apply.</td><td>Language Object (see at the bottom of the table)</td><td><p>{</p><p>"en": "flexibleDescription comes here",</p><p>"ja": "自由だーーー"</p><p>}</p></td></tr><tr><td>medium<mark style="color:red;">*</mark></td><td>Flexible language description object for the medium.</td><td>Language Object (see at the bottom of the table)</td><td><p>{</p><p>"en": "Oil on canvas",</p><p>"ja": "キャンバスに油彩",</p><p>"zh": "布面油画"</p><p>}</p></td></tr><tr><td>edition</td><td>Edition details</td><td>object</td><td><p>{</p><p>"uniqueness": "unique work",</p><p>"proofType": "ED",</p><p>"number": 1,</p><p>"totalNumber": 3,</p><p>"note": { "en": "some extra notes " }</p><p>}</p></td></tr><tr><td>edition.note</td><td>Note to add details to the Edition information</td><td>Language Object (see at the bottom of the table)</td><td><p>{</p><p>"en": "note comes here",</p><p>"ja": "自由だーーー"</p><p>}</p></td></tr><tr><td>edition.uniqueness</td><td>Uniqueness of artwork</td><td>enumerated String with one of the following values ["unique work", "non unique work", "unknown"]</td><td>unique work</td></tr><tr><td>edition.proofType</td><td><p>Proof type of edition: -ED Edition<br>-AP Artist Proof<br>-TP Trial Proof<br>-SP Special Proof<br>-HC Hors de Commerce<br>-Open Edition</p><p><br>Need to add it when the artwork is edition work.</p></td><td>enumerated String with one of the following values ["ED","AP","TP","SP","HC", "Open Edition"]</td><td>ED</td></tr><tr><td>edition.number</td><td>Edition number (out of total editions)</td><td>number (integer)</td><td>1</td></tr><tr><td>edition.totalNumber</td><td>Total number of editions</td><td>number (integer)</td><td>3</td></tr><tr><td>contractTerms</td><td>Object with contract details</td><td>Object</td><td><p>{</p><p>"royaltyRate": 15.7,</p><p>"fileURL": "https://startrail.io/whitepaper/startrail_wp_en_v1.1.pdf"</p><p>}</p></td></tr><tr><td>contractTerms.royaltyRate</td><td>Royalty rate percentage.</td><td>number (integer)</td><td>15.7</td></tr><tr><td>contractTerms.fileURL</td><td>URL where the contract terms file is stored</td><td>URL</td><td>https://startrail.io/whitepaper/startrail_wp_en_v1.1.pdf</td></tr><tr><td>thumbnailURL<mark style="color:red;">*</mark></td><td>URL where the artwork thumbnail is stored</td><td>URL</td><td><a href="https://storage.googleapis.com/opensea-prod.appspot.com/puffs/3.png">https://storage.googleapis.com/opensea-prod.appspot.com/puffs/3.png</a></td></tr><tr><td>isDigital</td><td>boolean indicator to specify that the work is a digital artwork.</td><td>boolean</td><td>true</td></tr><tr><td>digitalDataHash</td><td><mark style="color:red;">DEPRECATED</mark></td><td>-</td><td>-</td></tr><tr><td>digitalComponents</td><td><p><strong>ISSUE API USER should leave it UNDEFINED.</strong><br></p><p>Array of Digital file objects to specify digital components</p></td><td>Array of Digital File Objects (see at the bottom of the table)</td><td><p>[{</p><p>"hash": "sha256-247e4b904322a1dd0b148cd77e8627ec7d391251380880ab4621726ecb945ef5",</p><p>"category": "artwork"</p><p>}]</p></td></tr><tr><td>attachmentFiles</td><td><p><strong>ISSUE API USER should leave it UNDEFINED.</strong><br></p><p>Array of Digital file objects to specify attachment files.</p></td><td>Array of Digital File Objects (see at the bottom of the table)</td><td>[{ "hash": "sha256-247e4b904322a1dd0b148cd77e8627ec7d391251380880ab4621726ecb945ef5", "category": "certificate" }]</td></tr><tr><td>name<mark style="color:red;">*</mark></td><td><p>name for external Marketplaces such as Opensea.<br></p><p>OpenSea will show text that filled here regardless of the language.<br><br>If <code>name</code> is not defined by client, it will be auto-filled with thumbnailURL value.</p></td><td>string</td><td>An example of title</td></tr><tr><td>description</td><td><p>Description for external Marketplaces such as Opensea<br></p><p>Please refer to explanation for name field<br></p><p>If description is not designated, it will be auto-filled with size, medium, edition, contractTerms.fileURL.</p></td><td>string</td><td>Description of example NFT</td></tr><tr><td>image<mark style="color:red;">*</mark></td><td><p>Image URL for external Marketplaces such as Opensea.<br></p><p>If <code>image</code> is not defined by client, it will be auto-filled with thumbnailURL value.</p></td><td>URL</td><td><a href="https://storage.googleapis.com/opensea-prod.appspot.com/puffs/3.png">https://storage.googleapis.com/opensea-prod.appspot.com/puffs/3.png</a></td></tr><tr><td>external_url</td><td><p>External reference URL used for OpenSea.</p><p>if metadata.external_url is designated, it can have different URL from payload.externalUrls . if metadata.external_url is NOT designated, it will have the first URL from payload.externalUrls .</p></td><td>URL</td><td><a href="https://openseacreatures.io/3">https://openseacreatures.io/3</a></td></tr><tr><td>yearOfCreation<mark style="color:red;">*</mark></td><td>Flexible language description object for the year of the creation</td><td>Language Object (see at the bottom of the table)</td><td><p>{</p><p>"en": "around 2010-2020",</p><p>"ja": "2010年から2020年頃"</p><p>}</p></td></tr></tbody></table>

### Object

<table><thead><tr><th width="255">Attribute</th><th width="464">Description</th><th width="204">data format</th><th width="374">Example</th></tr></thead><tbody><tr><td>&#x3C;Language Object></td><td>An object specifying multiple supported languages. The property names are two letter letter language codes from BCP-47, such as en or ja.</td><td>Each property name: 2 char string from BCP-47, value: string</td><td><p>{</p><p>"en": "A title",</p><p>"ja": "タイトル",</p><p>"zh": "一个标题"</p><p>}</p></td></tr><tr><td>&#x3C;Digital File Object></td><td>An object providing details of digital files.</td><td>Object</td><td>{ "hash": "sha256-247e4b904322a1dd0b148cd77e8627ec7d391251380880ab4621726ecb945ef5", "category": "artwork", "URL":"https://some.url.com/file", "name":"filename.jpg" }</td></tr><tr><td>&#x3C;Digital File Object>.hash</td><td><p>ISSUE API USER should leave it UNDEFINED</p><p>sha256 Hash Hex String of the file.</p></td><td>sha256 Hash Hex String</td><td>sha256-f63238ce3b8c4f8a99fb453d716d5451f75508c2e403a58af0412014187e7a61</td></tr><tr><td>&#x3C;Digital File Object>.category</td><td>String describing the category of the file.</td><td>String to describe the category. Supported values in Startrail PORT: ”certificate”, “for_authenticity”,”artwork”,”installation”</td><td>artwork</td></tr><tr><td>&#x3C;Digital File Object>.URL</td><td>url where the file can be accessed</td><td>URL</td><td></td></tr><tr><td>&#x3C;Digital File Object>.name</td><td>name of the file</td><td>string</td><td></td></tr></tbody></table>

### Changes From the Previous Version

1. Deprecate `startbahnCertICTagUIDs` . This field should be defined as same as `chipUIDs`.
2. Change of `$schema`
3. Change of `$schemaIntegrity`

## Complete Example

```json
{
  "$schema": "https://api.startrail.io/api/v1/schema/registry-record-metadata.v2.1.schema.json",
  "$schemaIntegrity": "sha256-15f8e99eb9d4292287282942db2f2de9bbcc4761c555c6f7da23feec010c1221",
  "startbahnCertICTagUIDs": [
    "1234567890abcdef"
  ],
  "chipUIDs": [
    "1234567890abcdef"
  ],
  "title": {
    "en": "A title",
    "ja": "タイトル",
    "zh": "一个标题"
  },
  "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": "https://startrail.io/whitepaper/startrail_wp_en_v1.1.pdf"
  },
  "note": {
    "en": "note",
    "zh": "注意"
  },
  "thumbnailURL": "https://storage.googleapis.com/opensea-prod.appspot.com/puffs/3.png",
  "yearOfCreation": {
    "en": "around 2010-2020",
    "ja": "2010年から2020年頃"
  },
  "isDigital": true,
  "digitalDataHash": "sha256-247e4b904322a1dd0b148cd77e8627ec7d391251380880ab4621726ecb945ef5",
  "digitalComponents": [{
    "hash": "sha256-247e4b904322a1dd0b148cd77e8627ec7d391251380880ab4621726ecb945ef5",
    "category": "artwork"
  }],
  "attachmentFiles": [{
    "hash": "sha256-247e4b904322a1dd0b148cd77e8627ec7d391251380880ab4621726ecb945ef5",
    "category": "artwork"
  }],
  "name": "some nft name",
  "description": "some nft description",
  "image": "https://storage.googleapis.com/opensea-prod.appspot.com/puffs/3.png",
  "external_url": "https://openseacreatures.io/3"
}
```


# Version 2.0

## Attributes

<mark style="color:red;">`*`</mark> is required.

<table><thead><tr><th width="255">Attribute</th><th width="464">Description</th><th width="204">data format</th><th width="374">Example</th></tr></thead><tbody><tr><td>$schema<mark style="color:red;">*</mark></td><td>URL to the schema json the given metadata follows. Fixed value for Metadata Version 2.0 in example value.</td><td>URL</td><td><a href="https://api.startrail.io/api/v1/schema/registry-record-metadata.v2.0.schema.json">https://api.startrail.io/api/v1/schema/registry-record-metadata.v2.0.schema.json</a></td></tr><tr><td>$schemaIntegrity<mark style="color:red;">*</mark></td><td>sha256 Hash of the normalized metadata schema. Fixed value for Metadata Version 2.0 in example value.</td><td>SHA Hash Hex String</td><td>sha256-f63238ce3b8c4f8a99fb453d716d5451f75508c2e403a58af0412014187e7a61</td></tr><tr><td>startbahnCertICTagUIDs</td><td>Array containing the list of the Chip UIDs of a physical artwork</td><td>Array of Strings</td><td>["1234567890abcdef"]</td></tr><tr><td>title<mark style="color:red;">*</mark></td><td>Flexible language description object for title of the work.</td><td>Language Object (see at the bottom of the table)</td><td><p>{</p><p>"en": "A title",</p><p>"ja": "タイトル",</p><p>"zh": "一个标题"</p><p>}</p></td></tr><tr><td>size</td><td>Object describing the size</td><td>object</td><td><p>{ "width": 200.0,</p><p>"height": 400.0,</p><p>"depth": 12.4,</p><p>"unit": "pixel",</p><p>"flexibleDescription":</p><p>{</p><p>"en": "flexibleDescription comes here",</p><p>"ja": "自由だーーー"</p><p>}</p><p>}</p></td></tr><tr><td>size.height</td><td>height dimension number.</td><td>number</td><td>10.5</td></tr><tr><td>size.width</td><td>width dimension number.</td><td>number</td><td>10.5</td></tr><tr><td>size.depth</td><td>depth dimension number.</td><td>number</td><td>10.5</td></tr><tr><td>size.unit</td><td>string specifying the unit of the dimensional numbers.</td><td>enumerated String with one of the following values. ["mm","cm","m","in","ft","pixel"]</td><td>mm</td></tr><tr><td>size.flexibleDescription</td><td>Alternative way to describe the size of the artwork, if the others dont apply.</td><td>Language Object (see at the bottom of the table)</td><td><p>{</p><p>"en": "flexibleDescription comes here",</p><p>"ja": "自由だーーー"</p><p>}</p></td></tr><tr><td>medium<mark style="color:red;">*</mark></td><td>Flexible language description object for the medium.</td><td>Language Object (see at the bottom of the table)</td><td><p>{</p><p>"en": "Oil on canvas",</p><p>"ja": "キャンバスに油彩",</p><p>"zh": "布面油画"</p><p>}</p></td></tr><tr><td>edition</td><td>Edition details</td><td>object</td><td><p>{</p><p>"uniqueness": "unique work",</p><p>"proofType": "ED",</p><p>"number": 1,</p><p>"totalNumber": 3,</p><p>"note": { "en": "some extra notes " }</p><p>}</p></td></tr><tr><td>edition.note</td><td>Note to add details to the Edition information</td><td>Language Object (see at the bottom of the table)</td><td><p>{</p><p>"en": "note comes here",</p><p>"ja": "自由だーーー"</p><p>}</p></td></tr><tr><td>edition.uniqueness</td><td>Uniqueness of artwork</td><td>enumerated String with one of the following values ["unique work", "non unique work", "unknown"]</td><td>unique work</td></tr><tr><td>edition.proofType</td><td><p>Proof type of edition: -ED Edition<br>-AP Artist Proof<br>-TP Trial Proof<br>-SP Special Proof<br>-HC Hors de Commerce<br>-Open Edition</p><p><br>Need to add it when the artwork is edition work.</p></td><td>enumerated String with one of the following values ["ED","AP","TP","SP","HC", "Open Edition"]</td><td>ED</td></tr><tr><td>edition.number</td><td>Edition number (out of total editions)</td><td>number (integer)</td><td>1</td></tr><tr><td>edition.totalNumber</td><td>Total number of editions</td><td>number (integer)</td><td>3</td></tr><tr><td>contractTerms</td><td>Object with contract details</td><td>Object</td><td><p>{</p><p>"royaltyRate": 15.7,</p><p>"fileURL": "https://startrail.io/whitepaper/startrail_wp_en_v1.1.pdf"</p><p>}</p></td></tr><tr><td>contractTerms.royaltyRate</td><td>Royalty rate percentage.</td><td>number (integer)</td><td>15.7</td></tr><tr><td>contractTerms.fileURL</td><td>URL where the contract terms file is stored</td><td>URL</td><td>https://startrail.io/whitepaper/startrail_wp_en_v1.1.pdf</td></tr><tr><td>thumbnailURL<mark style="color:red;">*</mark></td><td>URL where the artwork thumbnail is stored</td><td>URL</td><td><a href="https://storage.googleapis.com/opensea-prod.appspot.com/puffs/3.png">https://storage.googleapis.com/opensea-prod.appspot.com/puffs/3.png</a></td></tr><tr><td>isDigital</td><td>boolean indicator to specify that the work is a digital artwork.</td><td>boolean</td><td>true</td></tr><tr><td>digitalDataHash</td><td><mark style="color:red;">DEPRECATED</mark></td><td>-</td><td>-</td></tr><tr><td>digitalComponents</td><td><p><strong>ISSUE API USER should leave it UNDEFINED.</strong><br></p><p>Array of Digital file objects to specify digital components</p></td><td>Array of Digital File Objects (see at the bottom of the table)</td><td><p>[{</p><p>"hash": "sha256-247e4b904322a1dd0b148cd77e8627ec7d391251380880ab4621726ecb945ef5",</p><p>"category": "artwork"</p><p>}]</p></td></tr><tr><td>attachmentFiles</td><td><p><strong>ISSUE API USER should leave it UNDEFINED.</strong><br></p><p>Array of Digital file objects to specify attachment files.</p></td><td>Array of Digital File Objects (see at the bottom of the table)</td><td>[{ "hash": "sha256-247e4b904322a1dd0b148cd77e8627ec7d391251380880ab4621726ecb945ef5", "category": "certificate" }]</td></tr><tr><td>name<mark style="color:red;">*</mark></td><td><p>name for external Marketplaces such as Opensea.<br></p><p>OpenSea will show text that filled here regardless of the language.<br><br>If <code>name</code> is not defined by client, it will be auto-filled with thumbnailURL value.</p></td><td>string</td><td>An example of title</td></tr><tr><td>description</td><td><p>Description for external Marketplaces such as Opensea<br></p><p>Please refer to explanation for name field<br></p><p>If description is not defined, it will be auto-filled with size, medium, edition, contractTerms.fileURL.</p></td><td>string</td><td>Description of example NFT</td></tr><tr><td>image<mark style="color:red;">*</mark></td><td><p>Image URL for external Marketplaces such as Opensea.<br></p><p>If <code>image</code> is not defined by client, it will be auto-filled with thumbnailURL value.</p></td><td>URL</td><td><a href="https://storage.googleapis.com/opensea-prod.appspot.com/puffs/3.png">https://storage.googleapis.com/opensea-prod.appspot.com/puffs/3.png</a></td></tr><tr><td>external_url</td><td><p>External reference URL used for OpenSea.</p><p>if metadata.external_url is designated, it can have different URL from payload.externalUrls . if metadata.external_url is NOT designated, it will have the first URL from payload.externalUrls .</p></td><td>URL</td><td><a href="https://openseacreatures.io/3">https://openseacreatures.io/3</a></td></tr><tr><td>yearOfCreation<mark style="color:red;">*</mark></td><td>Flexible language description object for the year of the creation</td><td>Language Object (see at the bottom of the table)</td><td><p>{</p><p>"en": "around 2010-2020",</p><p>"ja": "2010年から2020年頃"</p><p>}</p></td></tr></tbody></table>

### Object

<table><thead><tr><th width="255">Attribute</th><th width="464">Description</th><th width="204">data format</th><th width="374">Example</th></tr></thead><tbody><tr><td>&#x3C;Language Object></td><td>An object specifying multiple supported languages. The property names are two letter letter language codes from BCP-47, such as en or ja.</td><td>Each property name: 2 char string from BCP-47, value: string</td><td><p>{</p><p>"en": "A title",</p><p>"ja": "タイトル",</p><p>"zh": "一个标题"</p><p>}</p></td></tr><tr><td>&#x3C;Digital File Object></td><td>An object providing details of digital files.</td><td>Object</td><td>{ "hash": "sha256-247e4b904322a1dd0b148cd77e8627ec7d391251380880ab4621726ecb945ef5", "category": "artwork", "URL":"https://some.url.com/file", "name":"filename.jpg" }</td></tr><tr><td>&#x3C;Digital File Object>.hash</td><td><p>ISSUE API USER should leave it UNDEFINED</p><p>sha256 Hash Hex String of the file.</p></td><td>sha256 Hash Hex String</td><td>sha256-f63238ce3b8c4f8a99fb453d716d5451f75508c2e403a58af0412014187e7a61</td></tr><tr><td>&#x3C;Digital File Object>.category</td><td>String describing the category of the file.</td><td>String to describe the category. Supported values in Startrail PORT: ”certificate”, “for_authenticity”,”artwork”,”installation”</td><td>artwork</td></tr><tr><td>&#x3C;Digital File Object>.URL</td><td>url where the file can be accessed</td><td>URL</td><td></td></tr><tr><td>&#x3C;Digital File Object>.name</td><td>name of the file</td><td>string</td><td></td></tr></tbody></table>

## Complete Example

```json
{
  "$schema": "https://api.startrail.io/api/v1/schema/registry-record-metadata.v2.0.schema.json",
  "$schemaIntegrity": "sha256-f63238ce3b8c4f8a99fb453d716d5451f75508c2e403a58af0412014187e7a61",
  "startbahnCertICTagUIDs": [
    "1234567890abcdef"
  ],
  "title": {
    "en": "A title",
    "ja": "タイトル",
    "zh": "一个标题"
  },
  "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": "https://startrail.io/whitepaper/startrail_wp_en_v1.1.pdf"
  },
  "note": {
    "en": "note",
    "zh": "注意"
  },
  "thumbnailURL": "https://storage.googleapis.com/opensea-prod.appspot.com/puffs/3.png",
  "yearOfCreation": {
    "en": "around 2010-2020",
    "ja": "2010年から2020年頃"
  },
  "isDigital": true,
  "digitalDataHash": "sha256-247e4b904322a1dd0b148cd77e8627ec7d391251380880ab4621726ecb945ef5",
  "digitalComponents": [{
    "hash": "sha256-247e4b904322a1dd0b148cd77e8627ec7d391251380880ab4621726ecb945ef5",
    "category": "artwork"
  }],
  "attachmentFiles": [{
    "hash": "sha256-247e4b904322a1dd0b148cd77e8627ec7d391251380880ab4621726ecb945ef5",
    "category": "artwork"
  }],
  "name": "some nft name",
  "description": "some nft description",
  "image": "https://storage.googleapis.com/opensea-prod.appspot.com/puffs/3.png",
  "external_url": "https://openseacreatures.io/3"
}
```


# Transfer

A SRR can contain various transfer information that characterizes the work, in addition to the history of the artwork’s ownership, i.e. who transferred it from whom. The information will be displayed on the SRR Viewer page.


# Version 1.2

## Attributes

<mark style="color:red;">`*`</mark> is required.

<table><thead><tr><th width="255">Attribute</th><th width="464">Description</th><th width="204">data format</th><th width="374">Example</th></tr></thead><tbody><tr><td>$schema*</td><td>IPFS URL to the schema json the given metadata follows. Fixed value for Metadata Version 1.2 in example value.</td><td>URL</td><td><a href="ipfs://bafkreiagmzvya63vrv4byglrtkabk5xrr2x7g7zsa3fzxbz43c7tyw6kgm">ipfs://bafkreiagmzvya63vrv4byglrtkabk5xrr2x7g7zsa3fzxbz43c7tyw6kgm</a></td></tr><tr><td>transferType*</td><td>Transfer type</td><td>"Primary sale" | "Secondary sale" | "Other transfer"</td><td>Primary sale</td></tr><tr><td>version</td><td>Semantic version of this schema.</td><td>string</td><td>1.2</td></tr><tr><td>remarks</td><td>Remarks in multiple languages</td><td>Language Object (see at the bottom of the table)</td><td>"remarks": { "en": "Reason for the transfer, English", "ja": "移転の理由：日本語" }</td></tr><tr><td>customHistoryId</td><td>id of custom history</td><td>number</td><td>10</td></tr></tbody></table>

### Object

<table><thead><tr><th width="255">Attribute</th><th width="464">Description</th><th width="204">data format</th><th width="374">Example</th></tr></thead><tbody><tr><td>&#x3C;Language Object></td><td>An object specifying multiple supported languages. The property names are two letter letter language codes from BCP-47, such as en or ja.</td><td>Each property name: 2 char string from BCP-47, value: string</td><td><p>{</p><p>"en": "A title",</p><p>"ja": "タイトル",</p><p>"zh": "一个标题"</p><p>}</p></td></tr></tbody></table>

## Complete Example

```json
{
  "$schema": "ipfs://bafkreiagmzvya63vrv4byglrtkabk5xrr2x7g7zsa3fzxbz43c7tyw6kgm",
  "transferType": "Primary sale",
  "customHistoryId": 1,
  "remarks": {
    "en": "Reason for the transfer, English",
    "ja": "移転の理由：日本語"
  }
}
```


# Version 1.1

## Attributes

<mark style="color:red;">`*`</mark> is required.

<table><thead><tr><th width="255">Attribute</th><th width="464">Description</th><th width="204">data format</th><th width="374">Example</th></tr></thead><tbody><tr><td>$schema*</td><td>URL to the schema json the given metadata follows. Fixed value for Metadata Version 1.1 in example value.</td><td>URL</td><td><a href="https://api.startrail.io/api/v1/schema/registry-record-transfer-metadata.v1.1.schema.json">https://api.startrail.io/api/v1/schema/registry-record-transfer-metadata.v1.1.schema.json</a></td></tr><tr><td>$schemaIntegrity*</td><td>sha256 Hash of the normalized metadata schema. Fixed value for Metadata Version 2.1 in example value.</td><td>SHA Hash Hex String</td><td>sha256-951deba4bdaf93442f00c2a993a246e8c181a2baa73a925d99a0dfe61e712c83</td></tr><tr><td>transferType*</td><td>Transfer type</td><td>"Primary sale" | "Secondary sale" | "Other transfer"</td><td>Primary sale</td></tr><tr><td>version</td><td>Semantic version of this schema.</td><td>string</td><td>1.2</td></tr><tr><td>remarks</td><td>Remarks in multiple languages</td><td>Language Object (see at the bottom of the table)</td><td>"remarks": { "en": "Reason for the transfer, English", "ja": "移転の理由：日本語" }</td></tr><tr><td>customHistoryId</td><td>id of custom history</td><td>number</td><td>10</td></tr></tbody></table>

### Object

<table><thead><tr><th width="255">Attribute</th><th width="464">Description</th><th width="204">data format</th><th width="374">Example</th></tr></thead><tbody><tr><td>&#x3C;Language Object></td><td>An object specifying multiple supported languages. The property names are two letter letter language codes from BCP-47, such as en or ja.</td><td>Each property name: 2 char string from BCP-47, value: string</td><td><p>{</p><p>"en": "A title",</p><p>"ja": "タイトル",</p><p>"zh": "一个标题"</p><p>}</p></td></tr></tbody></table>

## Complete Example

```json
{
  "$schema": "https://api.startrail.io/api/v1/schema/registry-record-transfer-metadata.v1.1.schema.json",
  "$schemaIntegrity": "sha256-951deba4bdaf93442f00c2a993a246e8c181a2baa73a925d99a0dfe61e712c83",
  "transferType": "Primary sale",
  "remarks": {
    "en": "Reason for the transfer, English",
    "ja": "移転の理由：日本語"
  }
}
```


# Version 1.0

## Attributes

<mark style="color:red;">`*`</mark> is required.

<table><thead><tr><th width="255">Attribute</th><th width="464">Description</th><th width="204">data format</th><th width="374">Example</th></tr></thead><tbody><tr><td>transferType*</td><td>Transfer type</td><td>"Primary sale" | "Secondary sale" | "Other transfer"</td><td>Primary sale</td></tr><tr><td>$schema</td><td>URL to the schema json the given metadata follows. Fixed value for Metadata Version 1.0 in example value.</td><td>URL</td><td><a href="https://api.startrail.io/api/v1/schema/registry-record-transfer-metadata.v1.0.schema.json">https://api.startrail.io/api/v1/schema/registry-record-transfer-metadata.v1.0.schema.json</a></td></tr><tr><td>version</td><td>Semantic version of this schema.</td><td>string</td><td>1.2</td></tr><tr><td>remarks</td><td>Remarks in multiple languages</td><td>Language Object (see at the bottom of the table)</td><td>"remarks": { "en": "Reason for the transfer, English", "ja": "移転の理由：日本語" }</td></tr><tr><td>customHistoryId</td><td>id of custom history</td><td>number</td><td>10</td></tr></tbody></table>

### Object

<table><thead><tr><th width="255">Attribute</th><th width="464">Description</th><th width="204">data format</th><th width="374">Example</th></tr></thead><tbody><tr><td>&#x3C;Language Object></td><td>An object specifying multiple supported languages. The property names are two letter letter language codes from BCP-47, such as en or ja.</td><td>Each property name: 2 char string from BCP-47, value: string</td><td><p>{</p><p>"en": "A title",</p><p>"ja": "タイトル",</p><p>"zh": "一个标题"</p><p>}</p></td></tr></tbody></table>

## Complete Example

```json
{
  "$schema": "https://api.startrail.io/api/v1/schema/registry-record-transfer-metadata.v1.0.schema.json",
  "transferType": "Primary sale",
  "remarks": {
    "en": "Reason for the transfer, English",
    "ja": "移転の理由：日本語"
  }
}
```


# Custom History

A SRR can contain various historical information that characterizes the work, in addition to the history of the artwork’s ownership, i.e. who transferred it from whom. The information will be displayed on the SRR Viewer page.


# Custom History of Exhibition

If the artwork registered in Startrail PORT has been exhibited at various venues and spaces, the exhibition history can be added to the issued SRR. This information will be written on the blockchain.


# Version 1.2

## Attributes

<mark style="color:red;">`*`</mark> is required.

<table><thead><tr><th width="255">Attribute</th><th width="464">Description</th><th width="204">data format</th><th width="374">Example</th></tr></thead><tbody><tr><td>$schema<mark style="color:red;">*</mark></td><td>IPFS URL to the schema JSON the given metadata follows. Fixed value for Metadata Version 1.2 in example value.</td><td>URL</td><td>ipfs://bafkreigj7co62qaqcyhrk35ttpc5pemxu55jxiefxgrkjkww7odh4wdvl4</td></tr><tr><td>historyType<mark style="color:red;">*</mark></td><td>History Type.</td><td>string</td><td>exhibition</td></tr><tr><td>hostLUW</td><td>Host LUW, Producer</td><td>string</td><td>0x8DFea3525EE810A7FEa886Fee69c57e68B5d5052</td></tr><tr><td>period<mark style="color:red;">*</mark></td><td>An object that contains time information of the custom history.</td><td>object</td><td>{ "from": "2020-01-23", "to": "2020-03-21" }</td></tr><tr><td>period.from<mark style="color:red;">*</mark></td><td>Period from</td><td>string</td><td>2020-01-23</td></tr><tr><td>period.to<mark style="color:red;">*</mark></td><td>Period to</td><td>string</td><td>2020-03-21</td></tr><tr><td>title<mark style="color:red;">*</mark></td><td>Title of the exhibition.<br></td><td>string</td><td><p>{</p><p>"en": "ABC Exhibition",</p><p>"ja": "エキシビション ABC"</p><p>}</p></td></tr><tr><td>venue<mark style="color:red;">*</mark></td><td>Venue of the exhibition.<br><mark style="color:red;">Required if it includes physical exhibition.</mark></td><td>string</td><td><p>{</p><p>"en": "Online",</p><p>"ja": "オンライン",</p><p>"zh": "一个标题"</p><p>}</p></td></tr><tr><td>hostCountry<mark style="color:red;">*</mark></td><td>Country where the exhibition held.<br><mark style="color:red;">Required if it includes physical exhibition.</mark></td><td>Enumerated string following ISO 3166-2 which is 2-letter code</td><td>CN</td></tr><tr><td>city</td><td>City</td><td>string</td><td>Beijing</td></tr><tr><td>isOnlineOnly</td><td>True if the exhibition is only held online, false if it is held physically</td><td>boolean</td><td>true</td></tr></tbody></table>

## Complete Example

```json
{
  "$schema": "ipfs://bafkreigj7co62qaqcyhrk35ttpc5pemxu55jxiefxgrkjkww7odh4wdvl4",
  "historyType": "exhibition",
  "hostLUW": "0x8DFea3525EE810A7FEa886Fee69c57e68B5d5052",
  "period": {
    "from": "2020-01-23",
    "to": "2020-03-21"
  },
  "title":  {
    "en": "ABC Exhibition",
    "ja": "エキシビション ABC"
  },
  "venue": {
    "en": "Online",
    "ja": "オンライン",
    "zh": "一个标题"
  },
  "hostCountry": "CN",
  "city": "Beijing"
}
```


# Custom History of Auction

Auction history of an artwork can be added to the issued SRR.


# Version 1.3

## Attributes

<mark style="color:red;">`*`</mark> is required

<table><thead><tr><th width="255">Attribute</th><th width="464">Description</th><th width="204">data format</th><th width="374">Example</th></tr></thead><tbody><tr><td>$schema<mark style="color:red;">*</mark></td><td>IPFS URL to the schema JSON the given metadata follows. Fixed value for Metadata Version 1.3 in example value.</td><td>URL</td><td>ipfs://bafkreigscdxuga3bisqbacawavo7yt5ta27okfydppsiy26wbtiiguw52u</td></tr><tr><td>historyType<mark style="color:red;">*</mark></td><td>History Type.</td><td>string</td><td>auction</td></tr><tr><td>hostLUW<mark style="color:red;">*</mark></td><td>Host LUW, Producer</td><td>string</td><td>0x8DFea3525EE810A7FEa886Fee69c57e68B5d5052</td></tr><tr><td>period<mark style="color:red;">*</mark></td><td>An object that contains time information of the custom history.</td><td>object</td><td>{ "from": "2020-01-23", "to": "2020-03-21" }</td></tr><tr><td>period.from<mark style="color:red;">*</mark></td><td>Period from</td><td>string</td><td>2020-01-23</td></tr><tr><td>period.to<mark style="color:red;">*</mark></td><td>Period to</td><td>string</td><td>2020-03-21</td></tr><tr><td>saleName<mark style="color:red;">*</mark></td><td>Name of the auction</td><td>string</td><td><p>{</p><p>"en": "ABC Exhibition",</p><p>"ja": "エキシビション ABC"</p><p>}</p></td></tr><tr><td>venue<mark style="color:red;">*</mark></td><td>Venue of the exhibition.<br><mark style="color:red;">Required if it includes physical auction.</mark></td><td>string</td><td><p>{</p><p>"en": "Online",</p><p>"ja": "オンライン",</p><p>"zh": "一个标题"</p><p>}</p></td></tr><tr><td></td><td></td><td></td><td></td></tr><tr><td>hostCountry<mark style="color:red;">*</mark></td><td>Country where the auction held.<br><mark style="color:red;">Required if it includes physical auction.</mark></td><td>Enumerated string following ISO 3166-2 which is 2-letter code</td><td>CN</td></tr><tr><td>city</td><td>City</td><td>string</td><td>Beijing</td></tr><tr><td>isOnlineOnly</td><td>True if the auction is only held online, false if it is held physically.</td><td>boolean</td><td>true</td></tr></tbody></table>

## Complete Example

```json
{
  "$schema": "ipfs://bafkreigscdxuga3bisqbacawavo7yt5ta27okfydppsiy26wbtiiguw52u",
  "historyType": "auction",
  "hostLUW": "0x8DFea3525EE810A7FEa886Fee69c57e68B5d5052",
  "period": {
    "from": "2020-01-23",
    "to": "2020-03-21"
  },
  "saleName": {
    "en": "ABC Auction",
    "ja": "オークション ABC"
  },
  "venue": {
    "en": "Online",
    "ja": "オンライン",
    "zh": "一个标题"
  },
  "hostCountry": "CN",
  "city": "Beijing"
}
```


# Custom History of Appraisal

Appraisal related history of an artwork can be added to the issued SRR.


# Version 1.1

## Attributes

<mark style="color:red;">`*`</mark> is required.

<table><thead><tr><th width="255">Attribute</th><th width="464">Description</th><th width="204">data format</th><th width="374">Example</th></tr></thead><tbody><tr><td>$schema<mark style="color:red;">*</mark></td><td>IPFS URL to the schema JSON the given metadata follows. Fixed value for Metadata Version 1.1 in example value.</td><td>URL</td><td>ipfs://bafkreiallgjahi2rxvl67p36n3e5vxay27uygpn62kayd3hnkcqjtqo7su</td></tr><tr><td>historyType<mark style="color:red;">*</mark></td><td>History Type.</td><td>string</td><td>appraisal</td></tr><tr><td>appraiserLUWs<mark style="color:red;">*</mark></td><td>Appraiser LUW</td><td>Array of string</td><td><p>[</p><p>"0x8DFea3525EE810A7FEa886Fee69c57e68B5d5052"</p><p>]</p></td></tr><tr><td>completionDate<mark style="color:red;">*</mark></td><td>Date of the completion</td><td>string</td><td>2020-01-23</td></tr><tr><td>attachmentFiles<mark style="color:red;">*</mark></td><td>Information regarding the proof files.</td><td>Array of object</td><td><p>[{</p><p>"hash": "sha256-247e4b904322a1dd0b148cd77e8627ec7d391251380880ab4621726ecb945ef5"</p><p>}]</p></td></tr><tr><td>attachmentFiles[*].hash<mark style="color:red;">*</mark></td><td>SHA256 of the proof files.</td><td>string</td><td>sha256-247e4b904322a1dd0b148cd77e8627ec7d391251380880ab4621726ecb945ef5</td></tr></tbody></table>

## Complete Example

```json


{
  "$schema": "ipfs://bafkreiallgjahi2rxvl67p36n3e5vxay27uygpn62kayd3hnkcqjtqo7su",
  "historyType": "appraisal",
  "appraiserLUWs": ["0x8DFea3525EE810A7FEa886Fee69c57e68B5d5052"],
  "completionDate": "2020-03-21",
  "attachmentFiles": [{
    "hash": "sha256-247e4b904322a1dd0b148cd77e8627ec7d391251380880ab4621726ecb945ef5"
  }]
}
```


# Custom History of Restoration

Restoration related history of an artwork can be added to the issued SRR.


# Version 1.0

## Attributes

<table><thead><tr><th width="255">Attribute</th><th width="464">Description</th><th width="204">data format</th><th width="374">Example</th></tr></thead><tbody><tr><td>$schema<mark style="color:red;">*</mark></td><td>IPFS URL to the schema JSON the given metadata follows. Fixed value for Metadata Version 1.1 in example value.</td><td>URL</td><td>ipfs://bafkreibjnxxlgz7myci2u6vpxqkqybiuahlmg4w7sqnajelovxktfsm5om</td></tr><tr><td>historyType<mark style="color:red;">*</mark></td><td>History Type.</td><td>string</td><td>restoration</td></tr><tr><td>restorerLUWs<mark style="color:red;">*</mark></td><td>Restorer LUW</td><td>Array of string</td><td><p>[</p><p>"0x8DFea3525EE810A7FEa886Fee69c57e68B5d5052"</p><p>]</p></td></tr><tr><td>period<mark style="color:red;">*</mark></td><td>An object that contains time information of the custom history.</td><td>object</td><td>{ "from": "2020-01-23", "to": "2020-03-21" }</td></tr><tr><td>period.from<mark style="color:red;">*</mark></td><td>Period from</td><td>string</td><td>2020-01-23</td></tr><tr><td>period.to<mark style="color:red;">*</mark></td><td>Period to</td><td>string</td><td>2020-03-21</td></tr><tr><td>attachmentFiles<mark style="color:red;">*</mark></td><td>Information regarding the proof files.</td><td>Array of object</td><td><p>[{</p><p>"hash": "sha256-247e4b904322a1dd0b148cd77e8627ec7d391251380880ab4621726ecb945ef5"</p><p>}]</p></td></tr><tr><td>attachmentFiles[*].hash<mark style="color:red;">*</mark></td><td>SHA256 of the proof files.</td><td>string</td><td>sha256-247e4b904322a1dd0b148cd77e8627ec7d391251380880ab4621726ecb945ef5</td></tr></tbody></table>

## Complete Example

```json
{
  "$schema": "ipfs://bafkreibjnxxlgz7myci2u6vpxqkqybiuahlmg4w7sqnajelovxktfsm5om",
  "historyType": "restoration",
  "restorerLUWs": ["0x8DFea3525EE810A7FEa886Fee69c57e68B5d5052"],
  "period": {
    "from": "2020-01-23",
    "to": "2020-03-21"
  },
  "attachmentFiles": [{
    "hash": "sha256-247e4b904322a1dd0b148cd77e8627ec7d391251380880ab4621726ecb945ef5"
  }]
}
```


# Custom History of Offchain

Offchain Metadata for Custom Histories Metadata Schema


# Version 1.1

## Version 1.1

### Attributes

<mark style="color:red;">`*`</mark> is required.

## Version 1.1

### Attributes

`*` is required.

<table><thead><tr><th width="255">Attribute</th><th width="464">Description</th><th width="204">data format</th><th width="374">Example</th></tr></thead><tbody><tr><td>$schema*</td><td>IPFS URL to the schema JSON the given metadata follows. Fixed value for Metadata Version 1.1 in example value.</td><td>URL</td><td>ipfs://bafkreifk5hh7lyov7vl26kjubcdybw2ymt4znklpj72ygl5x2i36smdfmu</td></tr><tr><td>historyType*</td><td>History Type</td><td>string</td><td>off_chain</td></tr><tr><td>linkedSRRs</td><td>the srrs that should be linked to this history by default</td><td>Array of string</td><td>["0x8DFea3525EE810A7FEa886Fee69c57e68B5d5052"]</td></tr><tr><td>attachmentFiles*</td><td>Information regarding the proof files.</td><td>Array of object</td><td>[{ "hash": "sha256-247e4b904322a1dd0b148cd77e8627ec7d391251380880ab4621726ecb945ef5", "title": "name_of_the_file.pdf", "url": "https://some.storage.com/file.path" }]</td></tr></tbody></table>

### Complete Example

```json
{
  "$schema": "ipfs://bafkreifk5hh7lyov7vl26kjubcdybw2ymt4znklpj72ygl5x2i36smdfmu",
  "historyType": "off_chain",
  "linkedSRRs": [{"srrId": "123456789012"}],
  "attachmentFiles": [{
    "hash": "sha256-247e4b904322a1dd0b148cd77e8627ec7d391251380880ab4621726ecb945ef5",
    "title": "name_of_the_file.pdf",
    "url": "https://some.storage.com/file.path"
  }]
}
```


# Get Owned SRRs

to get SRRs information by owner's address

<mark style="color:blue;">`GET`</mark> `<base_url>/port/api/v1/ownerAddress/{ownerAddress}/ownedSrrs`

Please replace `<base_url>` as explained [here](/readme/url-per-environment).

Get issued SRR(s) that are currently owned by the given address. SRR(s) are paginated. Check the default pagination query parameters.

{% hint style="info" %}
**Recommended: query the subgraph directly**

This endpoint is a thin convenience wrapper around the [Startrail subgraph](/subgraph/subgraph) plus an IPFS metadata fetch. For anything beyond a quick lookup we recommend querying the subgraph directly — it is the authoritative source for issuer / artist / collection / ownership / provenance information, and the SRR `metadata` JSON should be fetched from the [Startrail IPFS CDN gateway](/subgraph/ipfs-cdn-gateway) using the `metadataDigest` (CID) returned by the subgraph.
{% endhint %}

#### Path Parameters

| Name                                           | Type   | Description                                                                                     |
| ---------------------------------------------- | ------ | ----------------------------------------------------------------------------------------------- |
| ownerAddress<mark style="color:red;">\*</mark> | string | <p>owner address.</p><p><em>Example</em></p><p>: 0xdf2c421c4Bc5a5694D1AE4b400886E148b378630</p> |

#### Query Parameters

| Name  | Type   | Description                               |
| ----- | ------ | ----------------------------------------- |
| limit | number | <p><em>Default value</em></p><p>: 100</p> |
| page  | number | <p><em>Default value</em></p><p>: 1</p>   |

{% tabs %}
{% tab title="200: OK " %}
{% tabs %}
{% tab title="Description of SRR Data" %}
Array of SRR data sourced from the subgraph and aggregated with the on-chain metadata fetched from IPFS.

See [Description Of SRR Data](/get-srr-api/description-of-srr-data).
{% endtab %}

{% tab title="Example" %}

```json
[
  {
    "id": "0x7942627305545af0e6c826d54cc9b2c5d190a874-227890056407",
    "tokenId": "227890056407",
    "ownerAddress": "0x2B8A689885278012a7681C3A37aC33B9357eFA2F",
    "isPrimaryIssuer": true,
    "artistAddress": "0xd80228C535e52470C2034491cFE9dF1F840caFB9",
    "metadataDigest": "bafkreiabepvyxyetkcb3xbjo3ocuyfo6psv3rc3yj34hequwcsilyvjima",
    "transferCommitment": null,
    "lockExternalTransfer": false,
    "royaltyReceiver": "0xA6E6a9E20a541680a1D6E1412f5088AefBF58a22",
    "royaltyBasisPoints": 1570,
    "createdAt": "2023-08-12T10:21:33.000Z",
    "updatedAt": "2023-09-03T04:11:02.000Z",
    "issuer": {
      "id": "0xa6e6a9e20a541680a1d6e1412f5088aefbf58a22",
      "walletAddress": "0xA6E6a9E20a541680a1D6E1412f5088AefBF58a22",
      "salt": "0x...",
      "owners": ["0x..."],
      "originalName": "issuer original name",
      "englishName": "issuer english name",
      "userType": "handler",
      "threshold": 1,
      "createdAt": "2022-04-14T03:15:35.153Z",
      "updatedAt": "2022-04-14T03:15:35.153Z"
    },
    "artist": {
      "id": "0xd80228c535e52470c2034491cfe9df1f840cafb9",
      "walletAddress": "0xd80228C535e52470C2034491cFE9dF1F840caFB9",
      "salt": "0x...",
      "owners": ["0x..."],
      "originalName": "Willem de Kooning",
      "englishName": "William de Kooning",
      "userType": "artist",
      "threshold": 1,
      "createdAt": "2020-10-23T07:54:54.486Z",
      "updatedAt": "2020-10-23T07:54:54.486Z"
    },
    "collection": {
      "id": "0x7942627305545af0e6c826d54cc9b2c5d190a874",
      "name": "collection name",
      "symbol": "T22",
      "ownerAddress": "0xA6E6a9E20a541680a1D6E1412f5088AefBF58a22",
      "createdAt": "2023-08-01T06:00:00.000Z",
      "updatedAt": "2023-08-01T06:00:00.000Z"
    },
    "metadata": {
      // Raw SRR metadata JSON, exactly as fetched from the IPFS CDN gateway
      // (https://cdn.startrail.io/ipfs/<metadataDigest>).
      // See the Metadata Schema section for the full schema.
    },
    "metadataHistory": [
      {
        "metadataDigest": "bafkreiabepvyxyetkcb3xbjo3ocuyfo6psv3rc3yj34hequwcsilyvjima",
        "createdAt": "2023-08-12T10:21:33.000Z"
      }
    ],
    "history": [],
    "provenance": [],
    "customHistories": [],
    "transfers": []
  }
]
```

{% endtab %}
{% endtabs %}
{% endtab %}
{% endtabs %}

## Swagger Endpoint (Test Environment)

[Swagger to test](https://api-stg.startrail.startbahn.jp/port/api#/public/SRROwnerController_getOwnedSrrs).

## Required Permissions

No required permission. This endpoint is public as the information is also public.


# Get SRR by Collection contract address and Token Id

to get SRR data

<mark style="color:blue;">`GET`</mark> `<base_url>/port/api/v1/collection/{collectionContractAddress}/srr/{tokenId}`

Please replace `<base_url>` as explained [here](/readme/url-per-environment).

{% hint style="info" %}
**Recommended: query the subgraph directly**

This endpoint proxies a single SRR record from the [Startrail subgraph](/subgraph/subgraph). For most use cases — and especially when you need richer filtering, batching, or low latency — query the subgraph directly. The endpoint does **not** include the metadata JSON; fetch it from the [Startrail IPFS CDN gateway](/subgraph/ipfs-cdn-gateway) at `https://cdn.startrail.io/ipfs/<metadataDigest>`.
{% endhint %}

#### Path Parameters

| Name                                      | Type   | Description                                                                |
| ----------------------------------------- | ------ | -------------------------------------------------------------------------- |
| tokenId<mark style="color:red;">\*</mark> | string | TokenId of SRR. Example: 59531933                                          |
| collectionContractAddress                 | string | Ethereum address:, for example: 0xb3c438dCe59d2A1b6994E437d879ae00f24F2d34 |

{% tabs %}
{% tab title="200: OK " %}
Single SRR record sourced from the subgraph (without the IPFS-resident `metadata` JSON, `customHistories` or aggregated `transfers`). See [Description of SRR Data](/get-srr-api/description-of-srr-data) for the field reference.
{% endtab %}

{% tab title="404: Not Found" %}
The collection/tokenId pair is not present in the subgraph.
{% endtab %}
{% endtabs %}

## Swagger Endpoint (Test Environment)

[Swagger to test](https://api-stg.startrail.startbahn.jp/port/api#/public/SRRCollectionPublicController_getSRRByCollectionAndTokenId).

## No Permission is required

This endpoint is public and the information is also public.


# Get SRR by Token Id

\[deprecated] to get SRR data for tokens in Startrail Registry only. This endpoint is deprecated in favor of its parent record.

<mark style="color:blue;">`GET`</mark> `<base_url>/port/api/v1/srr/{tokenId}`

Please replace `<base_url>` as explained [here](/readme/url-per-environment).

{% hint style="danger" %}
This endpoint is deprecated and can be removed anytime in future. [get SRR by Collection contract address and Token Id](/get-srr-api/get-srr-by-token-id) is a more explicit version that covers the use case of this endpoint.
{% endhint %}

#### Path Parameters

| Name                                      | Type   | Description                       |
| ----------------------------------------- | ------ | --------------------------------- |
| tokenId<mark style="color:red;">\*</mark> | string | TokenId of SRR. Example: 59531933 |

{% tabs %}
{% tab title="200: OK " %}
{% tabs %}
{% tab title="Description" %}
Single object of SRR Data.

[Description of SRR Data.](/get-srr-api/description-of-srr-data)
{% endtab %}

{% tab title="Example" %}

```json
{
  "tokenId": "6460655",
  "status": "issued",
  "isPrimaryIssuer": true,
  "issuedAt": "2021-05-17T05:31:51.000Z",
  "createdAt": "2020-10-23T08:41:59.949Z",
  "updatedAt": "2021-05-17T05:32:16.000Z",
  "srOwnable": {
    "ownerAddress": "0x2B8A689885278012a7681C3A37aC33B9357eFA2F",
    "createdAt": "2021-01-27T02:36:00.457Z",
    "updatedAt": "2021-01-27T02:36:00.457Z"
  },
  "artist": {
    "contractAddress": "0xd80228C535e52470C2034491cFE9dF1F840caFB9",
    "originalName": "Willem de Kooning",
    "englishName": "William de Kooning",
    "userType": "artist",
    "createdAt": "2020-10-23T07:54:54.486Z",
    "updatedAt": "2020-10-23T07:54:54.486Z"
  },
  "metadata": {
    "digest": "7dd366baa375c83d8a379bb4af12fef77f04c2b6ec46e3dbcbf1902f9145263d",
    "json": {
	      // Check Response Data Description section below for Artwork Metadata
    },
  "customHistories": [
    {
      "id": 9,
      "digest": "11a023bffc0145c6d3fce17ca249eb3715bf2a931f73d769e975d90f4ecd15a9",
      "json": {
        "city": "Tokyo",
        "venue": {
          "en": "Online",
          "ja": "オンライン",
          "zh": "一个标题"
        },
        "period": {
          "to": "2020-03-21",
          "from": "2020-01-23"
        },
        "$schema": "https://api.startrail.io/api/v1/customHistories/metadata/custom-history-of-auction-metadata.schema.json",
        "hostLUW": "0x8DFea3525EE810A7FEa886Fee69c57e68B5d5052",
        "saleName": {
          "en": "AuctionM_201207",
          "ja": "オークション ABC"
        },
        "historyType": "auction",
        "hostCountry": "JP"
      },
      "historyType": "auction",
      "name": "AuctionM_201207",
      "collectionContractAddress": "0x5B8A689885278012a7681C3A37aC33B9357eFA2F",
      "collection": {
        "contractAddress": "0x5B8A689885278012a7681C3A37aC33B9357eFA2F",
        "name": "collection name",
        "symbol": "collection-symbol"
      },
      "createdAt": "2020-12-07T06:56:05.859Z",
      "updatedAt": "2020-12-07T06:56:05.859Z"
    }
  ]
}
```

{% endtab %}
{% endtabs %}
{% endtab %}
{% endtabs %}

## Swagger Endpoint (Test Environment)

[Swagger to test](https://api-stg.startrail.startbahn.jp/port/api#/public/SRRPublicController_getCertByTokenId).

## Required Permissions

No required permission. This endpoint is public and the information is also public.


# Description Of SRR Data

{% hint style="info" %}
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.
{% endhint %}

{% hint style="warning" %}
**Source of truth: subgraph + IPFS CDN gateway**

The fields below mirror the Startrail [subgraph](/subgraph/subgraph) schema. The Get SRR REST endpoints are thin convenience wrappers that proxy this data; for richer / faster / more flexible queries, query the subgraph directly.

The `metadata` JSON itself lives on IPFS and is fetched from the [Startrail IPFS CDN gateway](/subgraph/ipfs-cdn-gateway) (`https://cdn.startrail.io/ipfs/<metadataDigest>`). The same gateway also serves any image and attachment file CIDs that appear inside the metadata.
{% endhint %}

<mark style="color:red;">\*</mark> indicates field that will always exist

| Name                                                   | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          | Type      | Example             |
| ------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------- | ------------------- |
| id<mark style="color:red;">\*</mark>                   | Subgraph SRR id (`<collectionAddress>-<tokenId>`)                                                                                                                                                                                                                                                                                                                                                                                                                                                    | string    |                     |
| tokenId<mark style="color:red;">\*</mark>              | NFT token ID                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         | string    |                     |
| ownerAddress                                           | Current owner EOA / contract address.                                                                                                                                                                                                                                                                                                                                                                                                                                                                | string    |                     |
| isPrimaryIssuer                                        | true if it's primary sales                                                                                                                                                                                                                                                                                                                                                                                                                                                                           | boolean   |                     |
| artistAddress                                          | Artist LUW wallet address                                                                                                                                                                                                                                                                                                                                                                                                                                                                            | string    |                     |
| metadataDigest<mark style="color:red;">\*</mark>       | IPFS CID of the SRR metadata JSON. Resolve via the [Startrail IPFS CDN gateway](/subgraph/ipfs-cdn-gateway) at `https://cdn.startrail.io/ipfs/<metadataDigest>`.                                                                                                                                                                                                                                                                                                                                     | string    | `bafkrei…`          |
| transferCommitment                                     | If set, the SRR currently has a pending transfer commitment hash.                                                                                                                                                                                                                                                                                                                                                                                                                                    | string    |                     |
| lockExternalTransfer                                   | If true, standard ERC721 transfer methods are disabled for this SRR.                                                                                                                                                                                                                                                                                                                                                                                                                                 | boolean   |                     |
| royaltyReceiver                                        | EIP-2981 royalty receiver address.                                                                                                                                                                                                                                                                                                                                                                                                                                                                   | string    |                     |
| royaltyBasisPoints                                     | EIP-2981 royalty basis points (e.g. `1570` = 15.70%).                                                                                                                                                                                                                                                                                                                                                                                                                                                | number    |                     |
| createdAt<mark style="color:red;">\*</mark>            | Time when SRR was minted on chain.                                                                                                                                                                                                                                                                                                                                                                                                                                                                   | Date      |                     |
| updatedAt<mark style="color:red;">\*</mark>            | Time when SRR data was last updated on chain.                                                                                                                                                                                                                                                                                                                                                                                                                                                        | Date      |                     |
| issuer.walletAddress<mark style="color:red;">\*</mark> | Issuer LUW wallet address                                                                                                                                                                                                                                                                                                                                                                                                                                                                            | string    |                     |
| issuer.originalName<mark style="color:red;">\*</mark>  | Issuer name (originalName)                                                                                                                                                                                                                                                                                                                                                                                                                                                                           | string    |                     |
| issuer.englishName<mark style="color:red;">\*</mark>   | Issuer name (EN)                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     | string    |                     |
| issuer.userType<mark style="color:red;">\*</mark>      | User type                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            | string    | 'artist', 'handler' |
| issuer.salt                                            | LUW salt                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             | string    |                     |
| issuer.owners                                          | EOA owners of the LUW                                                                                                                                                                                                                                                                                                                                                                                                                                                                                | string\[] |                     |
| issuer.threshold                                       | Multisig threshold of the LUW                                                                                                                                                                                                                                                                                                                                                                                                                                                                        | number    |                     |
| issuer.createdAt<mark style="color:red;">\*</mark>     | Time when issuer LUW was created                                                                                                                                                                                                                                                                                                                                                                                                                                                                     | Date      |                     |
| issuer.updatedAt<mark style="color:red;">\*</mark>     | Time when issuer LUW was updated                                                                                                                                                                                                                                                                                                                                                                                                                                                                     | Date      |                     |
| artist.walletAddress<mark style="color:red;">\*</mark> | Artist LUW wallet address                                                                                                                                                                                                                                                                                                                                                                                                                                                                            | string    |                     |
| artist.originalName<mark style="color:red;">\*</mark>  | Artist name (originalName)                                                                                                                                                                                                                                                                                                                                                                                                                                                                           | string    |                     |
| artist.englishName<mark style="color:red;">\*</mark>   | Artist name (EN)                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     | string    |                     |
| artist.userType<mark style="color:red;">\*</mark>      | User type                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            | string    | 'artist', 'handler' |
| artist.salt                                            | LUW salt                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             | string    |                     |
| artist.owners                                          | EOA owners of the LUW                                                                                                                                                                                                                                                                                                                                                                                                                                                                                | string\[] |                     |
| artist.threshold                                       | Multisig threshold of the LUW                                                                                                                                                                                                                                                                                                                                                                                                                                                                        | number    |                     |
| artist.createdAt<mark style="color:red;">\*</mark>     | Time when artist LUW was created                                                                                                                                                                                                                                                                                                                                                                                                                                                                     | Date      |                     |
| artist.updatedAt<mark style="color:red;">\*</mark>     | Time when artist LUW was updated                                                                                                                                                                                                                                                                                                                                                                                                                                                                     | Date      |                     |
| collection                                             | Collection the SRR belongs to. Absent for SRRs not issued under a custom collection.                                                                                                                                                                                                                                                                                                                                                                                                                 | object    |                     |
| collection.id                                          | Collection contract address (lowercased).                                                                                                                                                                                                                                                                                                                                                                                                                                                            | string    |                     |
| collection.name                                        | Name of collection                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   | string    |                     |
| collection.symbol                                      | Symbol of collection                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 | string    |                     |
| collection.ownerAddress                                | Owner LUW wallet address of the collection                                                                                                                                                                                                                                                                                                                                                                                                                                                           | string    |                     |
| collection.createdAt                                   | Time when collection was created                                                                                                                                                                                                                                                                                                                                                                                                                                                                     | Date      |                     |
| collection.updatedAt                                   | Time when collection was last updated                                                                                                                                                                                                                                                                                                                                                                                                                                                                | Date      |                     |
| metadata                                               | <p>Raw SRR metadata JSON fetched from IPFS via the Startrail IPFS CDN gateway.<br><br>Only present on endpoints that aggregate the metadata (e.g. <a href="/pages/Zw64KJiFT5upn7IxhswK">Get Owned SRRs</a>). For <a href="/pages/qlXBPGW7iA7bCtQeBfrc">Get SRR by Token Id</a>, fetch it yourself from <code>[https://cdn.startrail.io/ipfs/\&#x3C;metadataDigest>](https://cdn.startrail.io/ipfs/\&#x3C;metadataDigest>)</code>. See <a href="/pages/Ll08JupmVgJ6aILTc8tT">Metadata Schema</a>.</p> | Object    |                     |
| metadataHistory                                        | History of metadata changes (each entry has its own `metadataDigest` CID).                                                                                                                                                                                                                                                                                                                                                                                                                           | Array     |                     |
| history                                                | History of custom-history attachments to the SRR. See [Custom Histories](/metadata-schema/custom-history).                                                                                                                                                                                                                                                                                                                                                                                           | Array     |                     |
| provenance                                             | Subgraph provenance entries (raw on-chain transfer events). See [Transfer](/metadata-schema/transfer).                                                                                                                                                                                                                                                                                                                                                                                               | Array     |                     |
| customHistories                                        | Aggregated custom histories (only on aggregating endpoints).                                                                                                                                                                                                                                                                                                                                                                                                                                         | Array     |                     |
| transfers                                              | Aggregated provenance / transfers with metadata (only on aggregating endpoints).                                                                                                                                                                                                                                                                                                                                                                                                                     | Array     |                     |

Example

```json
{
  "id": "0x7942627305545af0e6c826d54cc9b2c5d190a874-227890056407",
  "tokenId": "227890056407",
  "ownerAddress": "0x2B8A689885278012a7681C3A37aC33B9357eFA2F",
  "isPrimaryIssuer": true,
  "artistAddress": "0xd80228C535e52470C2034491cFE9dF1F840caFB9",
  "metadataDigest": "bafkreiabepvyxyetkcb3xbjo3ocuyfo6psv3rc3yj34hequwcsilyvjima",
  "transferCommitment": null,
  "lockExternalTransfer": false,
  "royaltyReceiver": "0xA6E6a9E20a541680a1D6E1412f5088AefBF58a22",
  "royaltyBasisPoints": 1570,
  "createdAt": "2023-08-12T10:21:33.000Z",
  "updatedAt": "2023-09-03T04:11:02.000Z",
  "issuer": {
    "id": "0xa6e6a9e20a541680a1d6e1412f5088aefbf58a22",
    "walletAddress": "0xA6E6a9E20a541680a1D6E1412f5088AefBF58a22",
    "salt": "0x...",
    "owners": ["0x..."],
    "originalName": "issuer original name",
    "englishName": "issuer english name",
    "userType": "handler",
    "threshold": 1,
    "createdAt": "2022-04-14T03:15:35.153Z",
    "updatedAt": "2022-04-14T03:15:35.153Z"
  },
  "artist": {
    "id": "0xd80228c535e52470c2034491cfe9df1f840cafb9",
    "walletAddress": "0xd80228C535e52470C2034491cFE9dF1F840caFB9",
    "salt": "0x...",
    "owners": ["0x..."],
    "originalName": "Willem de Kooning",
    "englishName": "William de Kooning",
    "userType": "artist",
    "threshold": 1,
    "createdAt": "2020-10-23T07:54:54.486Z",
    "updatedAt": "2020-10-23T07:54:54.486Z"
  },
  "collection": {
    "id": "0x7942627305545af0e6c826d54cc9b2c5d190a874",
    "name": "collection name",
    "symbol": "T22",
    "ownerAddress": "0xA6E6a9E20a541680a1D6E1412f5088AefBF58a22",
    "createdAt": "2023-08-01T06:00:00.000Z",
    "updatedAt": "2023-08-01T06:00:00.000Z"
  },
  "metadata": {
    // Raw SRR metadata JSON, fetched from the Startrail IPFS CDN gateway
    // at https://cdn.startrail.io/ipfs/<metadataDigest>.
    // See the Metadata Schema section for the full schema.
  },
  "metadataHistory": [
    {
      "metadataDigest": "bafkreiabepvyxyetkcb3xbjo3ocuyfo6psv3rc3yj34hequwcsilyvjima",
      "createdAt": "2023-08-12T10:21:33.000Z"
    }
  ],
  "history": [],
  "provenance": [],
  "customHistories": [],
  "transfers": []
}
```


# Ethereum Signature Validator API

Authenticates SDK users from their EOAs

### **Features**

* Provide HTTP API endpoint
* Return the result of validation with error message

### **How to use API**

* Call HTTP API endpoint with arguments set in request body

## Authenticates SDK users from their EOAs

<mark style="color:orange;">`PUT`</mark> `https://asia-northeast1-startrail-api-prod.cloudfunctions.net/ethereum-signature-validator`

This module recovers Ethereum address (EOA) from message and signature, and validates that the recovered address is equal to the signer's address.

#### Request Body

| Name                                        | Type   | Description                                                                                                                                                                                                                                  |
| ------------------------------------------- | ------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| message<mark style="color:red;">\*</mark>   | String | raw message that signature is generated from (it does not have to be hashing)                                                                                                                                                                |
| signature<mark style="color:red;">\*</mark> | String | 0x prefixed signature that is generated by secp256k1 algorithm such as eth\_sign function                                                                                                                                                    |
| address<mark style="color:red;">\*</mark>   | String | Ethreum Address(EOA) of the signer(Both lowercase and mixed case which is compatible with EIP55 are accepted)                                                                                                                                |
| signMethod                                  | String | <p><code>eth\_sign</code>or <code>personal\_sign</code><br></p><p>A specific sign method with which signature is generated. If not specified, both <code>eth\_sign</code> and <code>personal\_sign</code> method is used to validate EOA</p> |

{% tabs %}
{% tab title="200: OK Validation has succeeded with valid input. Check `isValid` field in the response." %}

```javascript
// Validation Success
{ isValid: true, invalidReason: null, triedSignMethods: ['eth_sign'] }

// Validation Failure
{ isValid: false, invalidReason: 'Signature is invalid', triedSignMethods: ['eth_sign', 'personal_sign'] }
```

{% endtab %}

{% tab title="400: Bad Request Validation has failed with invalid input" %}

```javascript
// Validation Failure
{ isValid: false, invalidReason: 'Request parameters are not properly set' }

{ isValid: false, invalidReason: 'Signature format is invalid' }

{ isValid: false, invalidReason: 'Ethereum address is invalid' }
```

{% endtab %}
{% endtabs %}

## **Sample Request command**

```javascript
curl -X PUT \
  -H "Content-type: application/json" \
  --data '{"message": "test-message", "signature": "0x633bc7c201cf45fff0c1724b77c90c825a04c1b818f043915e2fedd55b4cfe681b425fe844ea3684a3860dc250527e318d3225b2d21ad0f75e419db4f0939a1b1c", "address": "0xBFC1331F8111102A51588b3b4A3E2F317B3a0363"}' \
 https://asia-northeast1-startrail-api-prod.cloudfunctions.net/ethereum-signature-validator
```

```javascript
curl -X PUT \
  -H "Content-type: application/json" \
  --data '{"message": "test-message", "signature": "0x7cdf2aa9dfb2da8c6f8fe63269b4dbfb923451f0e36087479e7f2897718a6d0b6438bd44e367907e30c65a4b950c9e11f94049d38a0ca1dd4d419e107a3dade31c", "address": "0xBFC1331F8111102A51588b3b4A3E2F317B3a0363", "signMethod": "personal_sign"}' \
  https://asia-northeast1-startrail-api-prod.cloudfunctions.net/ethereum-signature-validator
```


# Change Logs

@May 10, 2022

## Who needs to check it? <a href="#id-8faeef12-dd88-45aa-a4d4-f587a567ed06" id="id-8faeef12-dd88-45aa-a4d4-f587a567ed06"></a>

All who uses Ethereum Signature Validator API when this update is released.

## Release plan <a href="#edcd1d9b-8ffc-4487-8475-f091a657d5f7" id="edcd1d9b-8ffc-4487-8475-f091a657d5f7"></a>

Staging Environment: 23 of May

Production Environment: 30 of May

## What is updated? <a href="#id-783e3fdd-a37d-4163-b24b-627a57cb9329" id="id-783e3fdd-a37d-4163-b24b-627a57cb9329"></a>

* A new property `signMethod?: 'eth_sign' | 'personal_sign'` is added in request.
* A new property `triedSignMethods?: string[]` is added in response.

{% hint style="info" %}
User can now specify either <mark style="color:red;">eth\_sign</mark> or <mark style="color:red;">personal\_sign</mark> in request body for EOA recovery logic. If not specified, both methods are used for validation.
{% endhint %}

### Before <a href="#ea486e36-7f93-4b9f-b6aa-5da4b3c2135d" id="ea486e36-7f93-4b9f-b6aa-5da4b3c2135d"></a>

```javascript
**Request Body Type**

`{message: string, signature: string, address: string }`

**Response Type**

`{ isValid: boolean, invalidReason: string | null }`
```

### After <a href="#a849945b-9a5e-452f-b153-b0be9c8a0b1f" id="a849945b-9a5e-452f-b153-b0be9c8a0b1f"></a>

```javascript
**Request Body Type**

`{message: string, signature: string, address: string, signMethod?: 'eth_sign' | 'personal_sign' }`

**Response Type**

`{ isValid: boolean, invalidReason: string | null, triedSignMethods?: string[] }`
```

#### Example of request <a href="#id-68b53a97-c00b-4b49-8023-f98cc4652579" id="id-68b53a97-c00b-4b49-8023-f98cc4652579"></a>

**"signMethod": "eth\_sign" / Adding "signMethod": "eth\_sign"**

```javascript

curl -X PUT -H "Content-type: application/json" --data '{"message": "test-message", "signature": "0x633bc7c201cf45fff0c1724b77c90c825a04c1b818f043915e2fedd55b4cfe681b425fe844ea3684a3860dc250527e318d3225b2d21ad0f75e419db4f0939a1b1c", "address": "0xBFC1331F8111102A51588b3b4A3E2F317B3a0363", "signMethod": "eth_sign"}' http://localhost:8080/
```

**"signMethod": "personal\_sign" / Adding "personal\_sign": "eth\_sign"**

```javascript
curl -X PUT -H "Content-type: application/json" --data '{"message": "test-message", "signature": "0x7cdf2aa9dfb2da8c6f8fe63269b4dbfb923451f0e36087479e7f2897718a6d0b6438bd44e367907e30c65a4b950c9e11f94049d38a0ca1dd4d419e107a3dade31c", "address": "0xBFC1331F8111102A51588b3b4A3E2F317B3a0363", "signMethod": "personal_sign"}' http://localhost:8080/
```

**No signMethod: Validation result is the same as before.**

```javascript
curl -X PUT -H "Content-type: application/json" --data '{"message": "test-message", "signature": "0x633bc7c201cf45fff0c1724b77c90c825a04c1b818f043915e2fedd55b4cfe681b425fe844ea3684a3860dc250527e318d3225b2d21ad0f75e419db4f0939a1b1c", "addres
s": "0xBFC1331F8111102A51588b3b4A3E2F317B3a0363"}' http://localhost:8080/
```

#### Example of response <a href="#id-1ec7f77e-94df-464b-a81c-953879cd26f7" id="id-1ec7f77e-94df-464b-a81c-953879cd26f7"></a>

```javascript
{ isValid: true, invalidReason: null, triedSignMethods: ['eth_sign'] }
{ isValid: true, invalidReason: null, triedSignMethods: ['personal_sign'] }
{ isValid: false, invalidReason: 'Signature is invalid', triedSignMethods: ['eth_sign', 'personal_sign'] }
{ isValid: false, invalidReason: 'Signature format is invalid' }
```

{% hint style="info" %}
triedSignMethods property is not included the response if the validation process does not reach to signature validation part such as format issue as such.
{% endhint %}


# Introduction

Some of the key functionalities of the Startrail-Sdk-Js include:

* Provides simple access to EOA without crypto wallet and knowledge
* Provides the user information including a user's email with support for multiple social(SNS) and email based login
* Signs an arbitrary message with the Ethereum private key associated to your EOA
  * This can be used to connect the wallet to your website
* Executes blockchain transactions through our API with meta-transactions
  * Users sign [EIP-712 ](https://eips.ethereum.org/EIPS/eip-712)typed message, our API wraps it and broadcast. The signature given by the user cannot be compromized, and it gets unwrapped on-chain. There's no gas fee payment required by users

{% hint style="success" %}
See more details for our available [login providers details](/startrail-sdk-js/login-providers)
{% endhint %}

We will use the word SDK and Startrail-Sdk-Js interchangeably within rest of this document.

## 🤖 Built for AI coding tools

From **v2.2.0** the SDK ships first-class resources for LLMs and AI coding agents, so tools like Claude, Cursor and Copilot integrate it correctly with minimal context.

<table data-view="cards"><thead><tr><th></th><th></th><th></th></tr></thead><tbody><tr><td><strong>📄 llms.txt</strong></td><td>Machine-readable API reference — install, lifecycle, every method signature, the environment table and the error catalogue.</td><td>Bundled in the npm package:<br><code>node_modules/@startbahn/startrail-sdk-js/llms.txt</code></td></tr><tr><td><strong>🤖 AGENTS.md</strong></td><td>Rules and the canonical <code>construct → login → action</code> pattern for agents writing code against the SDK.</td><td>In the SDK source repository</td></tr><tr><td><strong>💬 Typed TSDoc</strong></td><td>Rich hover docs and autocomplete on the public class and request/response types, straight from your editor.</td><td>Bundled in the package types</td></tr></tbody></table>

{% hint style="info" %}
Point your AI assistant at `node_modules/@startbahn/startrail-sdk-js/llms.txt` for an accurate, version-matched reference instead of relying on its training data.
{% endhint %}

## Table Of Contents

* [Getting Started](/startrail-sdk-js/getting-started)
  * start development with SDK
* [Wallet Methods](/startrail-sdk-js/wallet-methods)
  * wallet based methods description
* [Startrail API Methods](/startrail-sdk-js/startrail-api-methods)
  * methods calling Startrail API from SDK
* [Login Providers](/startrail-sdk-js/login-providers)
  * social(SNS) and email based login details
* [Authentication Integration](/startrail-sdk-js/authentication-integration)
  * integrate user authentication with your backend system
* [Error Catalogue](/startrail-sdk-js/errors)
  * error responses from SDK
* [Change Logs](/startrail-sdk-js/change-logs)
  * concise summary of the changes, updates, and fixes made to the SDK


# Getting Started

To start development with Startrail-Sdk-Js

## NPM Package

{% embed url="<https://www.npmjs.com/package/@startbahn/startrail-sdk-js>" %}

| Env        | Tag                                                                |
| ---------- | ------------------------------------------------------------------ |
| Staging    | <https://www.npmjs.com/package/@startbahn/startrail-sdk-js/v/next> |
| Production | <https://www.npmjs.com/package/@startbahn/startrail-sdk-js>        |
|            |                                                                    |

## Add Packages <a href="#implementation" id="implementation"></a>

{% tabs %}
{% tab title="npm" %}

```
npm install @startbahn/startrail-sdk-js
```

{% endtab %}

{% tab title="yarn" %}

```
yarn add @startbahn/startrail-sdk-js
```

{% endtab %}
{% endtabs %}

## Script Tag <a href="#implementation" id="implementation"></a>

{% hint style="info" %}
Please ensure to check and update to the latest version when you are developing.
{% endhint %}

{% tabs %}
{% tab title="jsdeliver" %}

```
<script src="https://cdn.jsdelivr.net/npm/@startbahn/startrail-sdk-js@1.34.0/dist/startrail-sdk.min.js"></script>
```

{% endtab %}

{% tab title="unpkg" %}

```
<script src="https://unpkg.com/@startbahn/startrail-sdk-js@1.34.0"></script>
```

{% endtab %}
{% endtabs %}

## Import <a href="#implementation" id="implementation"></a>

```
const StartrailSdk = require('@startbahn/startrail-sdk-js').Startrail
// or
import StartrailSdk from "@startbahn/startrail-sdk-js";

const startrail = new StartrailSdk(config)
```

## Constructor

Configure and construct your Startrail SDK instance

```
new StartrailSdk(config)
```

### Properties

All the properties listed below are *optional*.

<table><thead><tr><th width="213">Parameter</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td>apiPath</td><td><code>string</code></td><td><p>Only define it if you want to do testing. For testing use the <code>Test</code> environment URL of STARTRAIL:</p><p><code>https://api-stg.startrail.startbahn.jp/api/v1</code></p><p>For production purpose do not define it.</p></td></tr><tr><td>wallet</td><td><code>'startrail' | 'metamask'</code></td><td><p>Wallet to activate. Default is <code>startrail.</code></p><ul><li><code>metamask</code>: Metamask wallet installed in users' environment.</li><li><code>startrail</code>: Google or Email/Password powered by Web3Auth.</li></ul></td></tr><tr><td>env</td><td><code>string</code></td><td><p>The environment of Startrail-Api endpoint, blockchain network and Web3Auth environment.<br></p><ul><li><code>production</code>: Polygon mainnet. TORUS production account and Startrail production Auth0 account. It is designed to communicate to Startrail production API.</li><li><code>staging</code>: Amoy testnet. TORUS production account and Startrail staging Auth0 account. It is designed to communicate to Startrail for testing.</li></ul><p><br></p></td></tr><tr><td>authAction</td><td><pre class="language-typescript"><code class="lang-typescript">{
  login: boolean
  signup: boolean
}
</code></pre></td><td>true hashes the preimage with keccak256</td></tr><tr><td>auth0ClientId</td><td><code>string</code></td><td>Client ID of Auth0 account</td></tr><tr><td>auth0Domain</td><td><code>string</code></td><td>Domain URL of Auth0</td></tr><tr><td>auth0TorusConfigKey</td><td><code>string</code></td><td>Config key name for TORUS initialisations to connect Auth0 domain and Web3Auth network. This key is only provided by Web3Auth</td></tr><tr><td>lang</td><td><pre class="language-typescript"><code class="lang-typescript"> 'ja' | 'en'
</code></pre></td><td>Language displayed on the UI</td></tr><tr><td>loginProvider</td><td><pre class="language-typescript"><code class="lang-typescript">['google' | 'email_passwordless' | 'facebook' | 'twitter' | 'line' | 'apple' | 'email_password']
</code></pre></td><td>login providers for connecting wallet, more detail <a href="/pages/WeiVC5tPvXJn8KxpzB0O">here</a>.</td></tr><tr><td>customUi</td><td><a href="/pages/ArKSzO1J1eosVczBoEWW"><code>CustomUI</code></a></td><td>Values to customise UI only under Startrail wallet powered by Web3Auth.<br><br>See more details in <a data-mention href="/pages/ArKSzO1J1eosVczBoEWW">/pages/ArKSzO1J1eosVczBoEWW</a> section.</td></tr><tr><td>callbackUrl</td><td><code>string</code></td><td>A URL to which a user is redirected after completing email verification with <code>email_password</code></td></tr><tr><td>withModal</td><td><code>boolean</code></td><td>Startrail modal powered by Web3Auth is opened instead of direct procedure with a specific login action.</td></tr><tr><td>rpcEndpoint</td><td><code>string</code></td><td>An <code>rpcEndpoint</code> that is accessed from the wallet you select. See for more detail <a href="/pages/MbQfOZXGMkqDaEe1RIN8">here</a>.</td></tr><tr><td>chainId</td><td><code>number</code></td><td>An <code>chainId</code> that is accessed from the wallet you select. See for more detail <a href="/pages/MbQfOZXGMkqDaEe1RIN8">here</a>.</td></tr><tr><td>mfaLevel</td><td><code>"none" | "default" | "optional" | "mandatory"</code></td><td>default is <code>none</code> that is not asking for MFA. Further detail is <a href="https://web3auth.io/docs/sdk/pnp/unreal/mfa#mfalevel">here</a>.</td></tr></tbody></table>

{% hint style="warning" %}
We recommend encouraging users to use this SDK in standard web browsers, such as Safari, Chrome, Firefox, etc. Some functionality of the SDK may not work properly in [web-views](https://en.wikipedia.org/wiki/WebView).
{% endhint %}

### Sample Config Values For Each Login Method

{% tabs %}
{% tab title="All" %}

```
// For Production (*No need to set auth0 or apiPath)
sdk = new Startrail({
  lang: 'ja',
  withModal: true,
  env: 'production',　// torusBuildEnv is deprecate after v1.25.0
  customUi: {
    logoUrl: 'https://yoursite.com/logo',
    serviceName: 'your service name'
  }
})

// For Development
sdk = new Startrail({
  apiPath: 'https://api-stg.startrail.startbahn.jp/api/v1',
  lang: 'ja',
  withModal: true,
  env: 'staging', // torusBuildEnv is deprecate after v1.25.0
  customUi: {
    logoUrl: 'https://yoursite.com/logo',
    serviceName: 'your service name'
  }
  // rpcEndpoint: 'your rpc endpoint url' // IF you want to designate endpoint
})
```

{% endtab %}

{% tab title="Google" %}

```
// For Production (*No need to set auth0 or apiPath)
sdk = new Startrail({
  lang: 'ja',
  loginProvider: ['google'],
  env: 'production',　// torusBuildEnv is deprecate after v1.25.0
  customUi: {
    logoUrl: 'https://yoursite.com/logo',
    serviceName: 'your service name'
  }
})

// For Development
sdk = new Startrail({
  apiPath: 'https://api-stg.startrail.startbahn.jp/api/v1',
  lang: 'ja',
  loginProvider: ['google'],
  env: 'staging', // torusBuildEnv is deprecate after v1.25.0
  customUi: {
    logoUrl: 'https://yoursite.com/logo',
    serviceName: 'your service name'
  }
  // rpcEndpoint: 'your rpc endpoint url' // IF you want to designate endpoint
})
```

{% endtab %}

{% tab title="Email Passwordless" %}

```
// For Production (*apiPath setting is not required)
sdk = new Startrail({
  lang: 'ja',
  loginProvider: ['email_passwordless'],
  env: 'production',　// torusBuildEnv is deprecate after v1.25.0
  customUi: {
    logoUrl: 'https://yoursite.com/logo',
    serviceName: 'your service name'
  }
})

// For Development
sdk = new Startrail({
  apiPath: 'https://api-stg.startrail.startbahn.jp/api/v1',
  lang: 'ja',
  loginProvider: ['email_passwordless'],
  env: 'staging', // torusBuildEnv is deprecate after v1.25.0
  customUi: {
    logoUrl: 'https://yoursite.com/logo',
    serviceName: 'your service name'
  }
  // rpcEndpoint: 'your rpc endpoint url' // IF you want to designate endpoint
})
```

{% endtab %}

{% tab title="Line" %}

```
// For Production (*apiPath setting is not required)
sdk = new Startrail({
  lang: 'ja',
  loginProvider: ['line'],
  env: 'production',　// torusBuildEnv is deprecate after v1.25.0
  customUi: {
    logoUrl: 'https://yoursite.com/logo',
    serviceName: 'your service name'
  }
})

// For Development
sdk = new Startrail({
  apiPath: 'https://api-stg.startrail.startbahn.jp/api/v1',
  lang: 'ja',
  loginProvider: ['line'],
  env: 'staging', // torusBuildEnv is deprecate after v1.25.0
  customUi: {
    logoUrl: 'https://yoursite.com/logo',
    serviceName: 'your service name'
  }
  // rpcEndpoint: 'your rpc endpoint url' // IF you want to designate endpoint
})
```

{% endtab %}

{% tab title="Facebook" %}

```
// For Production (*apiPath setting is not required)
sdk = new Startrail({
  lang: 'ja',
  loginProvider: ['facebook'],
  env: 'production',　// torusBuildEnv is deprecate after v1.25.0
  customUi: {
    logoUrl: 'https://yoursite.com/logo',
    serviceName: 'your service name'
  }
})

// For Development
sdk = new Startrail({
  apiPath: 'https://api-stg.startrail.startbahn.jp/api/v1',
  lang: 'ja',
  loginProvider: ['facebook'],
  env: 'staging', // torusBuildEnv is deprecate after v1.25.0
  customUi: {
    logoUrl: 'https://yoursite.com/logo',
    serviceName: 'your service name'
  }
  // rpcEndpoint: 'your rpc endpoint url' // IF you want to designate endpoint
})
```

{% endtab %}

{% tab title="Apple" %}

```
// For Production (*apiPath setting is not required)
sdk = new Startrail({
  lang: 'ja',
  loginProvider: ['apple'],
  env: 'production',　// torusBuildEnv is deprecate after v1.25.0
  customUi: {
    logoUrl: 'https://yoursite.com/logo',
    serviceName: 'your service name'
  }
})

// For Development
sdk = new Startrail({
  apiPath: 'https://api-stg.startrail.startbahn.jp/api/v1',
  lang: 'ja',
  loginProvider: ['apple'],
  env: 'staging', // torusBuildEnv is deprecate after v1.25.0
  customUi: {
    logoUrl: 'https://yoursite.com/logo',
    serviceName: 'your service name'
  }
  // rpcEndpoint: 'your rpc endpoint url' // IF you want to designate endpoint
})
```

{% endtab %}

{% tab title="Twitter" %}

```
// For Production (*apiPath setting is not required)
sdk = new Startrail({
  lang: 'ja',
  loginProvider: ['twitter'],
  env: 'production',　// torusBuildEnv is deprecate after v1.25.0
  customUi: {
    logoUrl: 'https://yoursite.com/logo',
    serviceName: 'your service name'
  }
})

// For Development
sdk = new Startrail({
  apiPath: 'https://api-stg.startrail.startbahn.jp/api/v1',
  lang: 'ja',
  loginProvider: ['twitter'],
  env: 'staging', // torusBuildEnv is deprecate after v1.25.0
  customUi: {
    logoUrl: 'https://yoursite.com/logo',
    serviceName: 'your service name'
  }
  // rpcEndpoint: 'your rpc endpoint url' // IF you want to designate endpoint
})
```

{% endtab %}

{% tab title="MetaMask" %}

```
// For Production (*No need to set auth0 or apiPath)
sdk = new Startrail({
  env: 'production',
  wallet: 'metamask'
})

// For Development
sdk = new Startrail({
  apiPath: 'https://api-stg.startrail.startbahn.jp/api/v1',
  env: 'staging',
  wallet: 'metamask'
})
```

{% endtab %}

{% tab title="Email Password" %}

```
// For Production (*No need to set auth0 or apiPath)
sdk = new Startrail({
  authAction: { login: false, signup: true },
  lang: 'ja',
  loginProvider: ['email_password'],
  env: 'production',　// torusBuildEnv is deprecate after v1.25.0
  redirectUrl: 'https://yoursite.com', // Not necessary for login
  customUi: {
    logoUrl: 'https://yoursite.com/logo',
    serviceName: 'your service name'
  }
})

// For Development
sdk = new Startrail({
　authAction: { login: false, signup: true },
　apiPath: 'https://api-stg.startrail.startbahn.jp/api/v1',
　lang: 'ja',
　loginProvider: ['email_password'],
　env: 'staging', // torusBuildEnv is deprecate after v1.25.0
　redirectUrl: 'https://yoursite.com', // Not necessary for login
　customUi: {
　　logoUrl: 'https://yoursite.com/logo',
　　serviceName: 'your service name'
　},
  // rpcEndpoint: 'your rpc endpoint url' // IF you want to designate endpoint
})
```

{% endtab %}
{% endtabs %}

To connect to your wallet, use `login` method

```
await sdk.login()
```

See more details in [Wallet Methods](/startrail-sdk-js/wallet-methods)

{% hint style="info" %}
Auth0, managed by Startbahn, is used for certain authentication methods, such as Email Password.
{% endhint %}


# RPC endpoint and chainId

While the default RPC endpoint and chainId are automatically configured in the StartrailSdk based on the environment you select, you can also overwrite them by specifying the values at the time of StartrailSdk instantiation. This is particularly useful in irregular cases that require immediate action or action without a StartrailSdk update, such as when

* a particular RPC endpoint experiences downtime
* the chainId is changed due to network deprecation

As the production environment is connected to the Polygon network while the staging environment is connected to the Amoy network, it's essential to provide the same rpcEndpoint as the network to which the StartrailSdk is connected.

{% hint style="warning" %}
It's important to note that Startrail smart contracts reside in the Polygon and Amoy blockchains, and changing the chainId to other networks is not supported.
{% endhint %}

### RPC endpoint

One of the following RPC endpoints is selected after a live status check from StartrailSdk, following the order from the top under the hood.

<table><thead><tr><th width="147.33333333333331">Env</th><th>Staging</th><th>Production</th></tr></thead><tbody><tr><td><strong>EVM network</strong></td><td><strong>Amoy</strong></td><td><strong>Polygon</strong></td></tr><tr><td><strong>Default RPC</strong></td><td><pre class="language-typescript"><code class="lang-typescript">https://polygon-amoy.infura.io/v3/693b51fa95334eb3bb1849da03cef748
</code></pre></td><td><pre><code>'https://polygon-rpc.com',
'https://rpc-mainnet.matic.network',
'https://matic-mainnet.chainstacklabs.com',
'https://rpc-mainnet.maticvigil.com',
'https://rpc-mainnet.matic.quiknode.pro',
'https://matic-mainnet-full-rpc.bwarelabs.com'
</code></pre></td></tr><tr><td>Default ChainId</td><td>80002</td><td>137</td></tr></tbody></table>

```javascript

sdk = new Startrail({
	...
  // example
  rpcEndpoint: 'https://YOUR_RPC_ENDPOINT',
  chainId: 123 // Select the chainId you want to overwrite.
})
```


# Wallet Methods

To know Web3 wallet relevant methods in Startrail-Sdk-Js

## `login`

Authenticate a user and allocate EOA.

Following arguments can be passed to overwrite the configuration originally set at the instantiation.

### Arguments

<table><thead><tr><th>Parameter</th><th>Type</th><th width="125">Mandatory<select><option value="d1c7a0ea01724bee9cdab675baee4c14" label="Optional" color="blue"></option></select></th><th>Description</th></tr></thead><tbody><tr><td>authAction</td><td><code>{ login: boolean signup: boolean }</code></td><td><span data-option="d1c7a0ea01724bee9cdab675baee4c14">Optional</span></td><td>Client ID of Auth0 account</td></tr><tr><td>lang</td><td><code>'ja' | 'en'</code></td><td><span data-option="d1c7a0ea01724bee9cdab675baee4c14">Optional</span></td><td>Language displayed on the UI</td></tr><tr><td>loginProvider</td><td><code>['google' | 'email_passwordless' | 'facebook' | 'twitter' | 'line' | 'apple' | 'email_password']</code></td><td><span data-option="d1c7a0ea01724bee9cdab675baee4c14">Optional</span></td><td>Login providers for SSO(Single Sign-On)</td></tr><tr><td>loginHint</td><td><code>string</code></td><td><span data-option="d1c7a0ea01724bee9cdab675baee4c14">Optional</span></td><td><strong>(v2.2.0+)</strong> The user's email when known in advance. With a single <code>email_passwordless</code> <code>loginProvider</code>, login starts the passwordless flow directly and skips the selection modal.</td></tr></tbody></table>

### Returns

`Promise<string[] | false>:` An object containing user information.

### Example

```
await startrailSdk().login()

// v2.2.0+ : skip the selection modal for a single email_passwordless provider
// by supplying the user's email as loginHint.
await startrailSdk().login({
  loginProvider: ['email_passwordless'],
  loginHint: 'user@example.com',
})
```

{% hint style="danger" %}
Distinct EOAs are assigned for each login provider, regardless of whether the same email is utilized for authentication methods, such as Google and Email Passwordless.

To prevent duplicate registration of accounts with the same email and different EOAs, it is recommended that your application rejects duplicate email registrations on your application side.
{% endhint %}

## `getUserInfo`

Get the logging-in user information.

### Returns

`Promise<UserInfo | false>:` The promise resolves upon the request success and rejects with a specific error code if the request fails.

| Value        | Type                                                                                                   | Description                                                                                                                                                    |
| ------------ | ------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| email        | `string`                                                                                               | <p>If selected social login allows email sharing.<br>See more details in<a data-mention href="/pages/WeiVC5tPvXJn8KxpzB0O">/pages/WeiVC5tPvXJn8KxpzB0O</a></p> |
| name         | `string`                                                                                               | If selected social login knows your name                                                                                                                       |
| profileImage | `string`                                                                                               | If selected social login knows your profile image                                                                                                              |
| typeOfLogin  | `'google' \| 'email_passwordless' \| 'facebook' \| 'twitter' \| 'line' \| 'apple' \| 'email_password'` | Selected social login type                                                                                                                                     |
| wallet       | `'startrail' \| 'metamask'`                                                                            | Selected wallet type                                                                                                                                           |
| verifier     | `string`                                                                                               | <p>Web3Auth only feature.<br>It determines your EOA.</p>                                                                                                       |
| verifierId   | `string`                                                                                               | <p>Web3Auth only feature.<br>Verifier Id of the logged in user if your selected wallets knows it</p>                                                           |
| isNewUser    | `boolean`                                                                                              | <p>Web3Auth only feature.<br>Returns if the logged in user is new to Torus wallet which supports Startrail login</p>                                           |

{% hint style="info" %}

```
Verifier is an identifier that determines EOA in Web3Auth network.
It is only returned under Web3Auth service.
```

{% endhint %}

See more details for the [Login Providers](/startrail-sdk-js/login-providers)

### Example

```
await startrailSdk().getUserInfo()
// Response example
// Google Login
{
  email: "sample@example.jp"
  name: "your Google account name"
  profileImage: "https://lh3.googleusercontent.com/a/image_on_google_account"
  typeOfLogin: "google"
  verifier: "google"
  verifierId: "sample@example.jp",
  wallet: "startrail",
  isNewUser: false // Returns if the logged in user is new to Torus wallet
}
// Email&Passwordless
{ 
  email: "sample@example.jp"
  name: "sample@example.jp"
  profileImage: "https://s.gravatar.com/avatar/image_of_default_auth0_icon.png"
  typeOfLogin: "email_passwordless"
  verifier: "torus-auth0-email-passwordless"
  verifierId: "sample@example.jp",
  wallet: "startrail",
  isNewUser: false // Returns if the logged in user is new to Torus wallet
}
```

## `signMessage`

Sign message with the Ethereum private key associated to your wallet

### `Arguments`

<table><thead><tr><th>Parameter</th><th>Type</th><th>Mandatory<select><option value="b9c67093cab14267a1884ca9333c6c79" label="Optional" color="blue"></option><option value="d9ccd44962294e47b0a04768cbea0fa3" label="Required" color="blue"></option></select></th><th>Description</th></tr></thead><tbody><tr><td>message</td><td><code>string</code></td><td><span data-option="d9ccd44962294e47b0a04768cbea0fa3">Required</span></td><td>A message to be signed</td></tr><tr><td>disableCustomPrefix</td><td><code>string</code></td><td><span data-option="b9c67093cab14267a1884ca9333c6c79">Optional</span></td><td>Web3Auth only feature to skip singing popup.<br>See more <a href="/pages/m3qEo4N0sRD0jyzWutu8">details here</a></td></tr><tr><td></td><td></td><td></td><td></td></tr></tbody></table>

### Returns

`Promise<{signature: string, prefix: string | false} | false>:`

### Example

```
await startrailSdk().signMessage('sample')
```

## `switchLanguage`

Switch language for Web3Auth wallet UI

### `Arguments`

<table><thead><tr><th>Parameter</th><th>Type</th><th>Mandatory<select><option value="b9c67093cab14267a1884ca9333c6c79" label="Optional" color="blue"></option><option value="d9ccd44962294e47b0a04768cbea0fa3" label="Required" color="blue"></option></select></th><th>Description</th></tr></thead><tbody><tr><td>lang</td><td><code>"ja" | "en"</code></td><td><span data-option="d9ccd44962294e47b0a04768cbea0fa3">Required</span></td><td>Web3Auth only feature to switch language</td></tr></tbody></table>

### Returns

`Promise<void | false>`

### Example

```
await startrailSdk().switchLanguage("ja")
```

## `logout`

Logout from the wallet.

### Returns

`Promise<void | false>` The promise resolves upon logout request success and rejects with a specific error code if the request fails.

### Example

```
await startrailSdk().logout()
```

{% hint style="info" %}

#### About Page reload and data persistency <a href="#ac1043f5-f618-4c0d-9782-d7c34b7a1b0f" id="ac1043f5-f618-4c0d-9782-d7c34b7a1b0f"></a>

* Wallet information is stored in browser session storage under Torus service domain.
* As SDK instance is gone at page reload, it's no longer available unless it is instantiated again. User, however, does not require additional login process to trigger its function call since the session between Torus node and Authenticator keeps maintained until sdk.logout function is called
  {% endhint %}


# Startrail API Methods

To know the methods calling to Startrail API from Startrail-Sdk-Js


# Add Custom Histories To SRRs

To know the methods calling to Startrail API from Startrail-Sdk-Js

## `addCustomHistoriesToSRRs()`

Add(Associate) custom history ids to SRRs

## Method parameters

| Variable                      | Type       | Description                                                                                                               |
| ----------------------------- | ---------- | ------------------------------------------------------------------------------------------------------------------------- |
| `startrailLUWContractAddress` | `string`   | The address of LicensedUserWallet(LUW) contract. Sets it when you want to execute transaction by the LUW contract address |
| `contractAddress`             | `string`   | The address of collection contract. Sets it when you want to associate collection contract address with the SRR.          |
| `tokenIds`                    | `string[]` | Startrail Registry Record Token IDs                                                                                       |
| `customHistoryIds`            | `string[]` | Custom History IDs                                                                                                        |

### Parameters Example

```
await sdk.addCustomHistoriesToSRRs(
  {
    startrailLUWContractAddress: '0x572a9e6B66F56A0D2c5cBE13066A4662b9C07868',
    tokenIds: ['556527153239', '832736095995'],
    customHistoryIds: ['12', '13']
  }
)
```

## Returns

`Promise` will be returned which resolves with a `Response` object upon a successful confirmation. `false` will be returned when user flow is cancelled in such a case that a user closes the popup modal.

If the confirmation fails, the `Promise` will resolve with an {error} object that describes the failure.

`Promise<Response | false>`

{% hint style="warning" %}
The use of "txReceiptId" will soon be deprecated and removed.
{% endhint %}

### Response

| Variable      | Type     | Description                                             |
| ------------- | -------- | ------------------------------------------------------- |
| `txReceiptId` | `string` | ID to identify transaction details in Startrail-API DB. |

### Error

Custom `Error` objects. Refer to the [Error Catalogue](/startrail-sdk-js/errors) for possible data.

### Response Example

###

Example

```
{
  txReceiptId: 0
}
```


# Approve SRR By Commitment

To know the methods calling to Startrail API from Startrail-Sdk-Js

## `approveSRRByCommitment()`

Approve transfer to those who knows the secret value set in approveSRRByCommitment() under a commit/reveal scheme.

{% hint style="info" %}
The approveSRRByCommitment() function alone does not initiate the transfer of the SRR. You need to call [Transfer SRR Ownership by RevealHash](/startrail-api/transfer-srr-ownership-by-revealhash) to finalize the transfer.
{% endhint %}

### How to complete ownership transfer with transferByReveal

When you provide a `preimage` for `approveSRRByCommitment`, you must hash the `preimage` using `keccak256` later on and provide the resulting hash as the `revealHash` parameter to the [`transferByReveal` endpoint](https://api-stg.startrail.startbahn.jp/api#/default/SRRController_transferByReveal) in order to complete the transfer. This is particularly useful in cases where the sender only knows the recipient's email, while the Dapp system, functioning as an intermediary, can determine the Ethereum address associated with that email.

When you provide a `revealHash` that has been hashed with keccak256 for `approveSRRByCommitment`, you can directly use it as the revealHash parameter for the [`transferByReveal` endpoint](https://api-stg.startrail.startbahn.jp/api#/default/SRRController_transferByReveal) to finalize the transfer.

### How to generate revealHash

```
import { bufferToHex, keccak256 } from 'ethereumjs-util'

const revealHash = bufferToHex(keccak256(Buffer.from('message')))
```

{% hint style="danger" %}
Please take care to ensure that the preimage remains sufficiently obscure to individuals who are not authorized. If it becomes predictable, anyone possessing this information will have the capability to execute the transfer.
{% endhint %}

## Method parameters

| Variable                      | Type               | Description                                                                                                                                                                                                                                                                                                     |
| ----------------------------- | ------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `startrailLUWContractAddress` | `string`           | The address of LicensedUserWallet(LUW) contract. Sets it when you want to execute transaction by the LUW contract address                                                                                                                                                                                       |
| `contractAddress`             | `string`           | The address of collection contract. Sets it when you want to associate collection contract address with the SRR.                                                                                                                                                                                                |
| `preimage`                    | `string`           | A string value used to reserve the transfer. Anyone who knows the value is entitled to complete the transfer. If this value is provided along with an email, confirmation emails will be sent to the email address at the time of transfer reservation and completion. This value is exclusive to `revealHash`. |
| `revealHash`                  | `string`           | <p>A keccak256 hash value used to reserve the transfer. Anyone who knows the value is entitled to complete the transfer. This value is exclusive to <code>preimage</code>.<br><br>See the sample code for Generating the revealHash Value below.</p>                                                            |
| `metadata`                    | `TransferMetadata` | SRR Transfer Metadata conforming to the [Transfer Data schema](/metadata-schema/transfer)                                                                                                                                                                                                                       |
| `isHashPreimageEnabled`       | `boolean`          | (Deprecated. It will be removed soon) Setting true hashes the preimage with keccak256                                                                                                                                                                                                                           |

### TransferMetadata

| Variable          | Type     | Description                                                                     |
| ----------------- | -------- | ------------------------------------------------------------------------------- |
| `transferType`    | `string` | Select one of the followings "Primary sale", "Secondary sale", "Other transfer” |
| `remarks`         | `Lang`   | Write down whatever relevant to transfer ownership                              |
| `customHistoryId` | `number` | Custom History Id that is already registered in Startrail                       |

### Lang

| Variable | Type     | Description  |
| -------- | -------- | ------------ |
| `ja`     | `string` | Japanse text |
| `en`     | `string` | English text |

### Sample Code for Generating the revealHash Value

```typescript
import { keccak256 } from '@ethersproject/keccak256'
export const generateRevealHash = (preimage: string): string => {
  return keccak256(Buffer.from(preimage))
}

// OR

import { keccak256 } from 'ethereumjs-util'
export const generateRevealHash = (preimage: string): string => {
  return bufferToHex(keccak256(Buffer.from(preimage)))
}
```

### Parameters Example

```
await sdk.approveSRRByCommitment(
  {
    tokenId: '41052235',
    preimage: 'art@tuta.io',
    metadata: {
      transferType: "Primary sale",
      remarks: {
        en: "Reason for the transfer",
        ja: "移転の理由：日本語"
      },
      customHistoryId: 1,
    },
  }
)
// OR
await sdk.approveSRRByCommitment(
  {
    tokenId: '41052235',
    revealHash: '0e675a836831dd887e5ad4ce4e8365b979dcd2141536d69e3767c7c620bbfc1f',
    metadata: {
      transferType: "Primary sale",
      remarks: {
        en: "Reason for the transfer",
        ja: "移転の理由：日本語"
      },
      customHistoryId: 1,
    },
  }
)
```

## Returns

`Promise` will be returned which resolves with a `Response` object upon a successful confirmation. `false` will be returned when user flow is cancelled in such a case that a user closes the popup modal.

If the confirmation fails, the `Promise` will resolve with an {error} object that describes the failure.

`Promise<Response | false>`

{% hint style="warning" %}
The use of "txReceiptId" will soon be deprecated and removed.
{% endhint %}

### Response

| Variable      | Type     | Description                                             |
| ------------- | -------- | ------------------------------------------------------- |
| `txReceiptId` | `string` | ID to identify transaction details in Startrail-API DB. |

### Error

Custom `Error` objects. Refer to the [Error Catalogue](/startrail-sdk-js/errors) for possible data.

### Response Example

###

Example

```
{
  txReceiptId: 0
}
```


# Bulk

To know the methods calling to Startrail API from Startrail-Sdk-Js

## `bulk()`

Bundling bulk transactions into a single Startrail-API call.

* `createSRR()`
* `approveSRRByCommitment()`
* `transferFromWithProvenance()`

## Method parameters

| Variable                      | Type        | Description                                                                                 |
| ----------------------------- | ----------- | ------------------------------------------------------------------------------------------- |
| `startrailLUWContractAddress` | `string`    | The address of LicensedUserWallet(LUW) contract.                                            |
| `isCompressEnabled`           | `boolean`   | Sets true if you want to compress a chunk of data with Gzip for HTTP call to Startrail-API. |
| `txs`                         | `TxDetails` | See TxDetails below.                                                                        |

### TxDetails

| Variable       | Type                                                                      | Description                                                                                                                                                                                                                                                                                                                                     |
| -------------- | ------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `functionType` | `'approveSRRByCommitment' \| 'createSRR' \| 'transferFromWithProvenance'` | Functoin type you want to execute                                                                                                                                                                                                                                                                                                               |
| `data`         | `approveSRRByCommitment \| createSRR \| transferFromWithProvenance`       | <p>Refer to the Method parameters respectively from<br><a href="/pages/4oopoohxg7HfT0tBze0z">createSRR</a><br><a href="/pages/XTXsUCYaHHmVYqkt0L4Y">approveSRRByCommitment</a><br><a href="/pages/FjqsD9gKgHGMcPXjOcZ1">transferFromWithProvenance</a><br>Please don't forget to extract 'startrailLUWContractAddress' from the parameters.</p> |

### Parameters Example

```
sdk.bulk(
  {
    isCompressEnabled: true,
    startrailLUWContractAddress: '0x1099a229951CeCcbE94aFA7017728503663E1983',
    txs: [
      {
        functionType: 'createSRR',
        data: {
          contractAddress: "0xA6E6a9E20a541680a1D6E1412f5088AefBF58a22",
          isPrimaryIssuer: false,
          artistAddress: "0xA6E6a9E20a541680a1D6E1412f5088AefBF58a22",
          lockExternalTransfer: false,
          metadata: {
            $schema: "ipfs://bafkreif77ionobe56gawqnplet46x3sjfinx3krm4reldzn7v6c2nfrmy4",
            startbahnCertICTagUIDs: [
              "1234567890abcdef"
            ],
            chipUIDs: [
              "1234567890abcdef"
            ],
            title: {
              en: "A title",
              ja: "タイトル",
              zh: "一个标题"
            },
            size: {
              width: 200.0,
              height: 400.0,
              depth: 12.4,
              unit: "pixel",
              flexibleDescription: {
                en: "flexibleDescription comes here",
                ja: "説明を入力"
              }
            },
            attributes: [
              {
                trait_type: "Mouth",
                value: "Surprised"
              }
            ],
            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: "ipfs://bafkreihmlsij6s5ri6e347h7yqjlsl4qa3iykub6qmumctb2mshc4u7vlm"
            },
            note: {
              en: "note",
              zh: "注意"
            },
            thumbnailURL: "ipfs://bafkreiedkaf4w5ogbbnfgp4jyrlvdqmwh3edryszcciee3puyn7nltcomi",
            yearOfCreation: {
              en: "around 2010-2020",
              ja: "2010年から2020年頃"
            },
            isDigital: true,
            digitalDataHash: "sha256-247e4b904322a1dd0b148cd77e8627ec7d391251380880ab4621726ecb945ef5",
            digitalComponents: [{
              hash: "sha256-247e4b904322a1dd0b148cd77e8627ec7d391251380880ab4621726ecb945ef5",
              category: "artwork"
            }],
            attachmentFiles: [{
              hash: "sha256-247e4b904322a1dd0b148cd77e8627ec7d391251380880ab4621726ecb945ef5",
              category: "artwork"
            }],
            name: "some nft name",
            description: "some nft description",
            image: "ipfs://bafkreiedkaf4w5ogbbnfgp4jyrlvdqmwh3edryszcciee3puyn7nltcomi",
            external_url: "https://openseacreatures.io/3"
          }
        }
      },
      {
        functionType: 'approveSRRByCommitment',
        data: {
          contractAddress: "0xA6E6a9E20a541680a1D6E1412f5088AefBF58a22",
          tokenId: '639283836913',
          preimage: 'test86@gmail.com',
          metadata: {
              $schema: 'ipfs://bafkreiagmzvya63vrv4byglrtkabk5xrr2x7g7zsa3fzxbz43c7tyw6kgm',
              transferType: 'Primary sale',
              remarks: {
                  en: 'Reason for the transfer, English',
                  ja: '移転の理由：日本語'
              }
          },
          isHashPreimageEnabled: false
        }
      },
      {
        functionType: 'transferFromWithProvenance',
        data: {
          contractAddress: "0xA6E6a9E20a541680a1D6E1412f5088AefBF58a22",
          to: '0xF6B90C96D53058123C32C53A03b4420594714342',
          tokenId: '832736095995',
          metadata: {
            transferType: "Primary sale",
            remarks: {
              en: "Reason for the transfer",
              ja: "移転の理由：日本語"
            },
            customHistoryId: 1,
          },
          isIntermediary: false,
        }
      }
    ]
  }
)

```

## Returns

`Promise` will be returned which resolves with a `Response` object upon a successful confirmation. `false` will be returned when user flow is cancelled in such a case that a user closes the popup modal.

If the confirmation fails, the `Promise` will resolve with an {error} object that describes the failure.

`Promise<Response | false>`

### Response

| Variable  | Type        | Description                                             |
| --------- | ----------- | ------------------------------------------------------- |
| `batchId` | `string`    | ID to identify transaction details in Startrail-API DB. |
| `txs`     | `TxDetails` | Transaction details                                     |

### TxDetails

| Variable      | Type     | Description              |
| ------------- | -------- | ------------------------ |
| `metadataCID` | `string` | A calculated metadataCID |
| `tokenId`     | `string` | A calculated tokenId     |

### Error

Custom `Error` objects. Refer to the [Error Catalogue](/startrail-sdk-js/errors) for possible data.

### Response Example

```
{
  batchId: 1,
  tx: {
      tokenId: "209850285627",
      metadataCID: "bafkreibjdtcklpe5wjgh6qp2dqygaydm3d4xttfhaphgian73bpprkr7xu"
  }
}
```


# Check ERC2981 Royalty

To know the methods calling to Startrail API from Startrail-Sdk-Js

## `checkERC2981Royalty()`

Retrieve royalty payment information for SRR that is in accordance with ERC-2981 for non-fungible tokens (NFTs).

Refer to ERC-2981: NFT Royalty Standard for more details.

## Method parameters

| Variable          | Type     | Description                                                                                                                                                                   |
| ----------------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `contractAddress` | `string` | The address of collection contract. Sets it when you want to associate collection contract address with the SRR.                                                              |
| `tokenId`         | `string` | Startrail Registry Record Token ID. Sets it when SRR is already issued and you know the tokenId.                                                                              |
| `metadata`        | `object` | Sets it instead of tokenId if SRR has not been issued yet. Refer to the [Startrail Registry (SRR) data schema](/metadata-schema/startrail-registry-srr) for all possible data |
| `issuerAddress`   | `string` | The ethereum address of the issuer of the artwork. Sets it instead of tokenId if SRR has not been issued yet.                                                                 |

### Parameters Example

```
sdk.checkERC2981Royalty(
  {  
    tokenId: '212786904920'
  }
)

```

## Returns

`Promise` will be returned which resolves with a `Response` object upon a successful confirmation. `false` will be returned when user flow is cancelled in such a case that a user closes the popup modal.

If the confirmation fails, the `Promise` will resolve with an {error} object that describes the failure.

`Promise<Response | false>`

### Response

| Variable             | Type             | Description                                                                                                             |
| -------------------- | ---------------- | ----------------------------------------------------------------------------------------------------------------------- |
| `royaltyReceiver`    | `string \| null` | The Etherem address of SRR Royalty receiver, null if its ERC2981 is not activated or ineligible                         |
| `royaltyBasisPoints` | `string \| null` | SRR royalty basis points, null if its ERC2981 is not activated or ineligible. Divide it by 100 to see the actual nmber. |

### Error

Custom `Error` objects. Refer to the [Error Catalogue](/startrail-sdk-js/errors) for possible data.

### Response Example

```
{
  "royaltyReceiver": "0xA6E6a9E20a541680a1D6E1412f5088AefBF58a22",
  "royaltyBasisPoints": "1570",
}
```


# Create Collection

To know the methods calling to Startrail API from Startrail-Sdk-Js

## `createCollection()`

Create a new SRR to a collection

## Method parameters

| Variable                      | Type     | Description                                                                                                               |
| ----------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------- |
| `startrailLUWContractAddress` | `string` | The address of LicensedUserWallet(LUW) contract. Sets it when you want to execute transaction by the LUW contract address |
| `name`                        | `string` | Collection name                                                                                                           |
| `symbol`                      | `string` | Collection symbol                                                                                                         |
| `salt`                        | `string` | The salt used in collection creation can also serve as an identifier for querying your collection data.                   |

### Parameters Example

```
await sdk.createCollection(
  {
    startrailLUWContractAddress: '0x572a9e6B66F56A0D2c5cBE13066A4662b9C07868',
    name: 'Taihei 2022',
    symbol: 'T22',
    salt: '0xea9369d265ddf31c12231b2aeb90662018499cb62117f30cf722bc1b76c62c46'
  }
)
```

## Returns

`Promise` will be returned which resolves with a `Response` object upon a successful confirmation. `false` will be returned when user flow is cancelled in such a case that a user closes the popup modal.

If the confirmation fails, the `Promise` will resolve with an {error} object that describes the failure.

`Promise<Response | false>`

{% hint style="warning" %}
The use of "txReceiptId" will soon be deprecated and removed.
{% endhint %}

### Response

| Variable      | Type        | Description                                             |
| ------------- | ----------- | ------------------------------------------------------- |
| `txReceiptId` | `string`    | ID to identify transaction details in Startrail-API DB. |
| `tx`          | `TxDetails` | Refer TX for all possible data                          |

### TxDetails

| Variable          | Type     | Description                                                                                             |
| ----------------- | -------- | ------------------------------------------------------------------------------------------------------- |
| `contractAddress` | `string` | The address of collection contract                                                                      |
| `salt`            | `string` | The salt used in collection creation can also serve as an identifier for querying your collection data. |

### Error

ErrorCustom `Error` objects. Refer to the [Error Catalogue](/startrail-sdk-js/errors) for possible data.

### Response Example

###

```
{
  txReceiptId: 0,
  tx: {
    contractAddress: '0xb135c5F2056e8D84a78094b1B02B28494845747F',
    salt: '0xea9369d265ddf31c12231b2aeb90662018499cb62117f30cf722bc1b76c62c46'
  }
}
```


# Convert Metadata

To know the methods calling to Startrail API from Startrail-Sdk-Js

## `convertMetadata()`

Metadata JSON is converted to the latest version according to the version schema

## Method parameters

| Variable        | Type              | Description                               |
| --------------- | ----------------- | ----------------------------------------- |
| `metadataBatch` | `MetadataBatch[]` | Refer MetadataBatch for all possible data |

### MetadataBatch

| Variable          | Type            | Description                                                                                                                                                                                                                               |
| ----------------- | --------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `metadata`        | `MetadataBatch` | Refer to the [Startrail Registry (SRR) data schema](/metadata-schema/startrail-registry-srr) for all possible data                                                                                                                        |
| `tokenId`         | `string`        | Startrail Registry Record Token ID                                                                                                                                                                                                        |
| `externalUrl`     | `string`        | External reference URL used in the metadata externalUrl field. Refer to the [Startrail Registry (SRR) data schema](/metadata-schema/startrail-registry-srr) for details.                                                                  |
| `artistName`      | `string`        | The artist's name used in the metadata description field. If the SRR already exists, we use the registered artist name instead. Refer to the [Startrail Registry (SRR) data schema](/metadata-schema/startrail-registry-srr) for details. |
| `issuerName`      | `string`        | The user's name used in the metadata description field. If the SRR already exists, we use the registered user name instead. Refer to the [Startrail Registry (SRR) data schema](/metadata-schema/startrail-registry-srr) for details.     |
| `contractAddress` | `string`        | The address of collection contract associated to the SRR                                                                                                                                                                                  |

### Parameters Example

```
await sdk.convertMetadata(
  {
    "metadataBatch": [
      {
        "metadata": {
          "$schema": "https://api.startrail.io/api/v1/schema/registry-record-metadata.v1.1.schema.json",
          "$schemaIntegrity": "sha256-fff288406b907ee6472585388bf519573628e45592be368f128b5b1e37a947c9",
          "startbahnCertICTagUIDs": [
            "1234567890abcdef"
          ],
          "title": {
            "en": "A title",
            "ja": "タイトル",
            "zh": "一个标题"
          },
          "size": {
            "width": 200,
            "height": 400,
            "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": "https://startrail.io/whitepaper/startrail_wp_en_v1.1.pdf"
          },
          "note": {
            "en": "note",
            "zh": "注意"
          },
          "thumbnailURL": "https://storage.googleapis.com/opensea-prod.appspot.com/puffs/3.png",
          "yearOfCreation": {
            "en": "around 2010-2020",
            "ja": "2010年から2020年頃"
          },
          "isDigital": true,
          "digitalDataHash": "sha256-247e4b904322a1dd0b148cd77e8627ec7d391251380880ab4621726ecb945ef5"
        },
        "artistName": "string",
        "externalUrl": "https://sample.com",
        "issuerName": "string",
        "tokenId": "string",
        "contractAddress": "string"
      }
    ]
  }
)

```

## Returns

`Promise` will be returned which resolves with a `Response` object upon a successful confirmation. `false` will be returned when user flow is cancelled in such a case that a user closes the popup modal.

If the confirmation fails, the `Promise` will resolve with an {error} object that describes the failure.

`Promise<Response[] | false>`

### Response

| Variable      | Type        | Description                                                                                                                            |
| ------------- | ----------- | -------------------------------------------------------------------------------------------------------------------------------------- |
| `metadata`    | `object`    | Converted metadata. Refer to the [Startrail Registry (SRR) data schema](/metadata-schema/startrail-registry-srr) for all possible data |
| `isConverted` | `TxDetails` | True is returned if metadata is converted                                                                                              |
| `message`     | `TxDetails` | Error message is returned if any.                                                                                                      |

### Error

Custom `Error` objects. Refer to the [Error Catalogue](/startrail-sdk-js/errors) for possible data.

### Response Example

```
{
  metadata: [
    {
      "$schema": "ipfs://bafkreif77ionobe56gawqnplet46x3sjfinx3krm4reldzn7v6c2nfrmy4",
      "startbahnCertICTagUIDs": [
        "1234567890abcdef"
      ],
      "chipUIDs": [
        "1234567890abcdef"
      ],
      "title": {
        "en": "A title",
        "ja": "タイトル",
        "zh": "一个标题"
      },
      "size": {
        "width": 200.0,
        "height": 400.0,
        "depth": 12.4,
        "unit": "pixel",
        "flexibleDescription": {
          "en": "flexibleDescription comes here",
          "ja": "自由だーーー"
        }
      },
      "attributes": [
        {
          "trait_type": "Mouth",
          "value": "Surprised"
        }
      ],
      "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": "https://startrail.io/whitepaper/startrail_wp_en_v1.1.pdf"
      },
      "note": {
        "en": "note",
        "zh": "注意"
      },
      "thumbnailURL": "https://storage.googleapis.com/opensea-prod.appspot.com/puffs/3.png",
      "yearOfCreation": {
        "en": "around 2010-2020",
        "ja": "2010年から2020年頃"
      },
      "isDigital": true,
      "digitalDataHash": "sha256-247e4b904322a1dd0b148cd77e8627ec7d391251380880ab4621726ecb945ef5",
      "digitalComponents": [{
        "hash": "sha256-247e4b904322a1dd0b148cd77e8627ec7d391251380880ab4621726ecb945ef5",
        "category": "artwork"
      }],
      "attachmentFiles": [{
        "hash": "sha256-247e4b904322a1dd0b148cd77e8627ec7d391251380880ab4621726ecb945ef5",
        "category": "artwork"
      }],
      "name": "some nft name",
      "description": "some nft description",
      "image": "https://storage.googleapis.com/opensea-prod.appspot.com/puffs/3.png",
      "external_url": "https://openseacreatures.io/3"
    }
  ],
  isConverted: false,
  message: 'Requested metadata is already up to date',
}
```


# Create SRR

To know the methods calling to Startrail API from Startrail-Sdk-Js

## `createSRR()`

Issue a new SRR on Startrail. Sends a transaction to Startrail smart contract via Startrail-API.

{% hint style="info" %}
Please call convertMetadata() in prior and replace with the converted metadata in order to keep the metadata always up to date [`convertMetadata()`](/startrail-sdk-js/startrail-api-methods/convertmetadata)
{% endhint %}

## Method parameters

| Variable                      | Type      | Description                                                                                                        |
| ----------------------------- | --------- | ------------------------------------------------------------------------------------------------------------------ |
| `startrailLUWContractAddress` | `string`  | The address of LicensedUserWallet(LUW) contract.                                                                   |
| `contractAddress`             | `string`  | The address of collection contract. Sets it when you want to associate collection contract address with the SRR.   |
| `isPrimaryIssuer`             | `boolean` | If you are the primary issuer of this NFT, set this to true                                                        |
| `artistAddress`               | `string`  | The ethereum address of the artist of the artwork.                                                                 |
| `metadata`                    | `object`  | Refer to the [Startrail Registry (SRR) data schema](/metadata-schema/startrail-registry-srr) for all possible data |
| `lockExternalTransfer`        | `boolean` | If you want to prevent your NFTs to be transferred on decentralized marketplaces, sets this to true                |

### Parameters Example

```
await sdk.approveSRRByCommitment(
  {  
    startrailLUWContractAddress: '0x113c6880fc4a2664E125973BC0Dfd37d62Ec7c3f',
    isPrimaryIssuer: true,
    artistAddress: '0x4a2B3Ca3c9C96898d1000137521427e9f4fdD019',
    lockExternalTransfer: false,
    metadata: {
      $schema: "ipfs://bafkreif77ionobe56gawqnplet46x3sjfinx3krm4reldzn7v6c2nfrmy4",
      startbahnCertICTagUIDs: [
        "1234567890abcdef"
      ],
      chipUIDs: [
        "1234567890abcdef"
      ],
      title: {
        en: "A title",
        ja: "タイトル",
        zh: "一个标题"
      },
      size: {
        width: 200.0,
        height: 400.0,
        depth: 12.4,
        unit: "pixel",
        flexibleDescription: {
          en: "flexibleDescription comes here",
          ja: "説明を入力"
        }
      },
      attributes: [
        {
          trait_type: "Mouth",
          value: "Surprised"
        }
      ],
      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: "ipfs://bafkreihmlsij6s5ri6e347h7yqjlsl4qa3iykub6qmumctb2mshc4u7vlm"
      },
      note: {
        en: "note",
        zh: "注意"
      },
      thumbnailURL: "ipfs://bafkreiedkaf4w5ogbbnfgp4jyrlvdqmwh3edryszcciee3puyn7nltcomi",
      yearOfCreation: {
        en: "around 2010-2020",
        ja: "2010年から2020年頃"
      },
      isDigital: true,
      digitalDataHash: "sha256-247e4b904322a1dd0b148cd77e8627ec7d391251380880ab4621726ecb945ef5",
      digitalComponents: [{
        hash: "sha256-247e4b904322a1dd0b148cd77e8627ec7d391251380880ab4621726ecb945ef5",
        category: "artwork"
      }],
      attachmentFiles: [{
        hash: "sha256-247e4b904322a1dd0b148cd77e8627ec7d391251380880ab4621726ecb945ef5",
        category: "artwork"
      }],
      name: "some nft name",
      description: "some nft description",
      image: "ipfs://bafkreiedkaf4w5ogbbnfgp4jyrlvdqmwh3edryszcciee3puyn7nltcomi",
      external_url: "https://openseacreatures.io/3"
    }
  },
)

```

## Returns

`Promise` will be returned which resolves with a `Response` object upon a successful confirmation. `false` will be returned when user flow is cancelled in such a case that a user closes the popup modal.

If the confirmation fails, the `Promise` will resolve with an {error} object that describes the failure.

`Promise<Response | false>`

Note: txReceiptId returns currently 0 in order to avoid slow response due to the congestion for blockchin mining.

### Response

| Variable      | Type        | Description                                             |
| ------------- | ----------- | ------------------------------------------------------- |
| `txReceiptId` | `string`    | ID to identify transaction details in Startrail-API DB. |
| `tx`          | `TxDetails` | Transaction details                                     |

### TxDetails

| Variable      | Type     | Description              |
| ------------- | -------- | ------------------------ |
| `metadataCID` | `string` | A calculated metadataCID |
| `tokenId`     | `string` | A calculated tokenId     |

### Error

Custom `Error` objects. Refer to the [Error Catalogue](/startrail-sdk-js/errors) for possible data.

### Response Example

```
{
  "txReceiptId": 0,
  "tx": {
      "tokenId": "209850285627",
      "metadataCID": "bafkreibjdtcklpe5wjgh6qp2dqygaydm3d4xttfhaphgian73bpprkr7xu"
  }
}
```


# Transfer Collection Ownership

To know the methods calling to Startrail API from Startrail-Sdk-Js

## `transferCollectionOwnership()`

Transfer Collection ownership to another address

## Method parameters

| Variable                      | Type     | Description                                                                                                               |
| ----------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------- |
| `startrailLUWContractAddress` | `string` | The address of LicensedUserWallet(LUW) contract. Sets it when you want to execute transaction by the LUW contract address |
| `contractAddress`             | `string` | The address of collection contract                                                                                        |
| `newOwner`                    | `string` | New address to transfer collection ownership to                                                                           |

### Parameters Example

```
await sdk.transferCollectionOwnership(
  {
    startrailLUWContractAddress: '0x572a9e6B66F56A0D2c5cBE13066A4662b9C07868',
    newOwner: '0x92657b061Ccead3C84EA1a99f1FEDc64Cc6b49E2',
    contractAddress: '0xb135c5F2056e8D84a78094b1B02B28494845747F',
  }
)
```

## Returns

`Promise` will be returned which resolves with a `Response` object upon a successful confirmation. `false` will be returned when user flow is cancelled in such a case that a user closes the popup modal.

If the confirmation fails, the `Promise` will resolve with an {error} object that describes the failure.

`Promise<Response | false>`

{% hint style="warning" %}
The use of "txReceiptId" will soon be deprecated and removed.
{% endhint %}

### Response

| Variable      | Type        | Description                                             |
| ------------- | ----------- | ------------------------------------------------------- |
| `txReceiptId` | `string`    | ID to identify transaction details in Startrail-API DB. |
| `tx`          | `TxDetails` | Refer TxDetails for all possible data                   |

### TxDetails

| Variable          | Type     | Description                        |
| ----------------- | -------- | ---------------------------------- |
| `contractAddress` | `string` | The address of collection contract |

### Error

Custom `Error` objects. Refer to the [Error Catalogue](/startrail-sdk-js/errors) for possible data.

### Response Example

```
{
  txReceiptId: 0,
  tx: {
    contractAddress: '0xb135c5F2056e8D84a78094b1B02B28494845747F',
  }
}
```


# Transfer SRR To Ethereum Address

To know the methods calling to Startrail API from Startrail-Sdk-Js

## `transferSRRToEthereumAddress()`

Transfer SRR to new owner directly designating etherem address.

## Method parameters

| Variable                      | Type               | Description                                                                                                               |
| ----------------------------- | ------------------ | ------------------------------------------------------------------------------------------------------------------------- |
| `startrailLUWContractAddress` | `string`           | The address of LicensedUserWallet(LUW) contract. Sets it when you want to execute transaction by the LUW contract address |
| `contractAddress`             | `string`           | The address of collection contract. Sets it when you want to associate collection contract address with the SRR.          |
| `to`                          | `string`           | Address to transfer for ownership to.                                                                                     |
| `metadata`                    | `TransferMetadata` | SRR Transfer Metadata conforming to [Transfer Data schema](/metadata-schema/transfer)                                     |
| `isIntermediary`              | `boolean`          | Intermediary flag used by startbahn to trigger a transaction. - defaults to false                                         |

### TransferMetadata

| Variable          | Type     | Description                                                                     |
| ----------------- | -------- | ------------------------------------------------------------------------------- |
| `transferType`    | `string` | Select one of the followings "Primary sale", "Secondary sale", "Other transfer” |
| `remarks`         | `Lang`   | Write down whatever relevant to transfer ownership                              |
| `customHistoryId` | `number` | Custom History Id that is already registered in Startrail                       |

### Lang

| Variable | Type     | Description  |
| -------- | -------- | ------------ |
| `ja`     | `string` | Japanse text |
| `en`     | `string` | English text |

### Parameters Example

```
await sdk.transferSRRToEthereumAddress(
  {
    contractAddress: '0x0c050f805Aa1ee2D4f9393365C95E299F1716fb1',
    startrailLUWContractAddress: '0x113c6880fc4a2664E125973BC0Dfd37d62Ec7c3f',
    to: '0xF6B90C96D53058123C32C53A03b4420594714342',
    tokenId: '832736095995',
    metadata: {
      transferType: "Primary sale",
      remarks: {
        en: "Reason for the transfer",
        ja: "移転の理由：日本語"
      },
      customHistoryId: 1,
    },
    isIntermediary: false,
  }
)
```

## Returns

`Promise` will be returned which resolves with a `Response` object upon a successful confirmation. `false` will be returned when user flow is cancelled in such a case that a user closes the popup modal.

If the confirmation fails, the `Promise` will resolve with an {error} object that describes the failure.

`Promise<Response | false>`

{% hint style="warning" %}
The use of "txReceiptId" will soon be deprecated and removed.
{% endhint %}

### Response

| Variable      | Type     | Description                                             |
| ------------- | -------- | ------------------------------------------------------- |
| `txReceiptId` | `string` | ID to identify transaction details in Startrail-API DB. |

### Error

Custom `Error` objects. Refer to the [Error Catalogue](/startrail-sdk-js/errors) for possible data.

### Response Example

```
{
  txReceiptId: 0
}
```


# Transfer From With Provenance

To know the methods calling to Startrail API from Startrail-Sdk-Js

## `transferFromWithProvenance()`

{% hint style="warning" %}
This function is deprecated. It will be replaced with transferSRRToEthereumAddress()
{% endhint %}

See more details in [Transfer SRR To Ethereum Address](/startrail-sdk-js/startrail-api-methods/transfersrrtoethereumaddress)


# Update Metadata

To know the methods calling to Startrail API from Startrail-Sdk-Js

## `updateMetadata()`

Update the metadata in the SRR.

## Method parameters

| Variable                      | Type     | Description                                                                                                        |
| ----------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------ |
| `startrailLUWContractAddress` | `string` | The address of LicensedUserWallet(LUW) contract.                                                                   |
| `contractAddress`             | `string` | The address of collection contract. Sets it when you want to associate collection contract address with the SRR.   |
| `tokenId`                     | `string` | Startrail Registry Record Token ID                                                                                 |
| `metadata`                    | `object` | Refer to the [Startrail Registry (SRR) data schema](/metadata-schema/startrail-registry-srr) for all possible data |

### Parameters Example

```
const res = await sdk.approveSRRByCommitment(
  {  
    startrailLUWContractAddress: '0x113c6880fc4a2664E125973BC0Dfd37d62Ec7c3f',
    tokenId: '212786904920',
    metadata: {
      "$schema": "https://api.startrail.io/api/v1/schema/registry-record-metadata.v2.0.schema.json",
      "$schemaIntegrity": "sha256-f63238ce3b8c4f8a99fb453d716d5451f75508c2e403a58af0412014187e7a61",
      "startbahnCertICTagUIDs": [
        "1234567890abcdef"
      ],
      "title": {
        "en": "A title",
        "ja": "タイトル",
        "zh": "一个标题"
      },
      "size": {
        "width": 200,
        "height": 400,
        "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": "https://startrail.io/whitepaper/startrail_wp_en_v1.1.pdf"
      },
      "note": {
        "en": "note",
        "zh": "注意"
      },
      "thumbnailURL": "https://storage.googleapis.com/opensea-prod.appspot.com/puffs/3.png",
      "yearOfCreation": {
        "en": "around 2010-2020",
        "ja": "2010年から2020年頃"
      },
      "isDigital": true,
      "digitalDataHash": "sha256-247e4b904322a1dd0b148cd77e8627ec7d391251380880ab4621726ecb945ef5",
      "digitalComponents": [
        {
          "hash": "sha256-247e4b904322a1dd0b148cd77e8627ec7d391251380880ab4621726ecb945ef5",
          "category": "artwork"
        }
      ],
      "attachmentFiles": [
        {
          "hash": "sha256-247e4b904322a1dd0b148cd77e8627ec7d391251380880ab4621726ecb945ef5",
          "category": "artwork"
        }
      ],
      "name": "some nft name",
      "description": "some nft description",
      "image": "https://storage.googleapis.com/opensea-prod.appspot.com/puffs/3.png",
      "external_url": "https://openseacreatures.io/3"
    }
  },
)

```

## Returns

`Promise` will be returned which resolves with a `Response` object upon a successful confirmation. `false` will be returned when user flow is cancelled in such a case that a user closes the popup modal.

If the confirmation fails, the `Promise` will resolve with an {error} object that describes the failure.

`Promise<Response | false>`

{% hint style="warning" %}
The use of "txReceiptId" will soon be deprecated and removed.
{% endhint %}

### Response

| Variable      | Type        | Description                                             |
| ------------- | ----------- | ------------------------------------------------------- |
| `txReceiptId` | `string`    | ID to identify transaction details in Startrail-API DB. |
| `tx`          | `TxDetails` | Transaction details                                     |

### TxDetails

| Variable          | Type     | Description                                              |
| ----------------- | -------- | -------------------------------------------------------- |
| `contractAddress` | `string` | The address of collection contract associated to the SRR |
| `metadataCID`     | `string` | A calculated metadataCID                                 |
| `tokenId`         | `string` | A calculated tokenId                                     |

### Error

Custom `Error` objects. Refer to the [Error Catalogue](/startrail-sdk-js/errors) for possible data.

### Response Example

```
{
  "txReceiptId": 0,
  "tx": {
      "contractAddress": "0x87Ef5da2c87e047E7F005Efb8b68a93Dc94D161c",
      "tokenId": "209850285627",
      "metadataCID": "bafkreibjdtcklpe5wjgh6qp2dqygaydm3d4xttfhaphgian73bpprkr7xu"
  }
}
```


# Login Providers

Available options for SNS and Email based login providers

SNS and Email-based login options enable you to connect your wallet without requiring in-depth knowledge of blockchain and cryptography. Here are the available options and additional details.

{% hint style="success" %}
Feel free to contact us if the authentication provider you are looking for is not on the list.
{% endhint %}

### Login Providers

SNS and Email-based authentications are powerd by Web3Auth library.

Users can obtain the same EOA across other applications such as Opensea or Rarible, that have implemented the same Web3Auth library.

{% hint style="danger" %}
Please be aware that Email Password is not accessible in any other applications that do not utilize Startrail-sdk-js. For new user registrations, please utilize Email Passwordless instead due to this limitation.
{% endhint %}

<table><thead><tr><th width="162.33333333333331">Provider</th><th>Available Values</th><th>Verifier on STG</th><th>Verifier on PROD</th></tr></thead><tbody><tr><td>Google</td><td><ul><li>user name</li><li>email</li><li>profile image</li></ul></td><td>google</td><td>google</td></tr><tr><td>LINE</td><td><ul><li>user name</li><li>profile image</li></ul></td><td>startrail-auth0-line-staging</td><td>startrail-auth0-line</td></tr><tr><td>Facebook</td><td><ul><li>user name</li><li>email</li><li>profile image</li></ul></td><td>facebook</td><td>facebook</td></tr><tr><td>Twitter</td><td><ul><li>user name</li><li>email</li><li>profile image</li></ul></td><td>torus-auth0-twitter</td><td>torus-auth0-twitter</td></tr><tr><td>Apple</td><td><ul><li>user name</li><li>email</li><li>profile image</li></ul></td><td>torus-auth0-apple</td><td>torus-auth0-apple</td></tr><tr><td>Email Passwordless</td><td><ul><li>email</li></ul></td><td>torus-auth0-email-passwordless</td><td>torus-auth0-email-passwordless</td></tr><tr><td>Email Password</td><td><ul><li>email</li><li>profile image</li></ul></td><td>startrail-auth0-email-password</td><td>startrail-auth0-staging</td></tr></tbody></table>


# Interface

UI of the SDK

## Common UI

Instead of directly proceeding with login, it is possible to open the modal for users to see the selectable social login list.

<table data-card-size="large" data-view="cards"><thead><tr><th></th><th></th><th></th></tr></thead><tbody><tr><td><h3>Login/Signup modal</h3></td><td><img src="/files/2LwRWhAN72eDDTJgo6dm" alt=""></td><td></td></tr><tr><td>When it's used ?</td><td><ul><li><code>sdk.login({isModal: true}) is called.</code></li></ul><p>This modal is not activated as default</p></td><td><ul><li>If you hide modal, you may want to put Terms of Service and its link on your application.</li><li>It's not possible to remove [View more options] dropdown.</li><li><p>The order of icons is arranged based on the order of the values in <code>loginProvider</code> array except Google, Email Password and Email Passwordless.</p><ul><li>For example, <code>loginProvider: ["google", "apple", "line"]</code> allocates the order from Google on the top followed by Apple and LINE underneath.</li></ul></li></ul></td></tr></tbody></table>

### Expected Parameters and Result with example case of google & line

<table><thead><tr><th width="175">withModal</th><th>[’google’]</th><th>[’google’,’line’]</th><th>undefined</th></tr></thead><tbody><tr><td>true</td><td>Modal: <code>google</code></td><td>Modal: <code>google</code>, <code>line</code></td><td>Modal: ALL</td></tr><tr><td>false</td><td>Direct Login without modal</td><td><code>Google</code> Login without modal</td><td><code>Google</code> Login without modal</td></tr><tr><td>undefined</td><td>Direct Login without modal</td><td>Modal: <code>google</code>, <code>line</code></td><td>Modal: ALL</td></tr></tbody></table>

### Email Passwordless without a modal (v2.2.0+)

Email Passwordless previously always opened the selection modal (the user still had to type their email there). From **v2.2.0**, if you configure a single `email_passwordless` provider **and** supply the user's email as `loginHint`, the SDK starts the passwordless flow directly and skips the modal entirely.

<table><thead><tr><th width="260">Configuration</th><th>Result</th></tr></thead><tbody><tr><td><code>loginProvider: ['email_passwordless']</code> + valid <code>loginHint</code></td><td>Passwordless flow starts directly — <strong>no modal</strong></td></tr><tr><td><code>loginProvider: ['email_passwordless']</code> without <code>loginHint</code> (or invalid email)</td><td>Modal opens as before</td></tr><tr><td>Multiple providers (even with <code>loginHint</code>)</td><td>Modal opens — the user picks a provider</td></tr></tbody></table>

{% code title="Email Passwordless — no modal" overflow="wrap" %}

```typescript
// Skip the modal: single email_passwordless provider + a known email
const sdk = new Startrail({
  env: 'staging',
  loginProvider: ['email_passwordless'],
  loginHint: 'user@example.com',
})
await sdk.login()

// loginHint can also be passed per-call on login()
await sdk.login({ loginProvider: ['email_passwordless'], loginHint: 'user@example.com' })
```

{% endcode %}

{% hint style="info" %}
`loginHint` is read from the constructor config or from the `login()` override. When you pass it to `login()` together with a single `email_passwordless` provider, the modal is skipped for that call.
{% endhint %}

<table data-card-size="large" data-view="cards"><thead><tr><th></th><th></th><th></th></tr></thead><tbody><tr><td><h3>Signature Modal</h3></td><td><img src="/files/vzrJ4uRg2XzN9vkWyL4r" alt=""></td><td></td></tr><tr><td></td><td>When it's used ?</td><td><ul><li><code>sdk.signMessage is called</code></li></ul><p>As default, <code>signMessage() is executed with an arbitrary string prepended to the message under the hood of Web3Auth library.</code></p><p><code>This enables skipping the signature modal popup and user consent process. The drawback of this is to require additional backend development. See more details.</code></p><p><a href="/pages/WXoze0Z6oXZIhSs0o8ih">Required implementation on clients’ backend</a><br><br>Web3Auth</p><p><a href="https://docs.tor.us/wallet/api-reference/installation">Installation | Documentation</a></p></td></tr></tbody></table>

### UI Samples with combination of parameters

<table data-view="cards" data-full-width="true"><thead><tr><th></th><th></th><th></th></tr></thead><tbody><tr><td><img src="/files/huNJ0EgNlLJJxapMfbeg" alt=""></td><td>{<code>loginProvider: ['email_password'], authAction: { login: true, signup: false }, withModal: true}</code></td><td></td></tr><tr><td><img src="/files/DBG9NKG14eZbzMbw6Vr5" alt=""></td><td>{<code>loginProvider: ['email_password'], authAction: { login: false, signup: true }, withModal: true}</code></td><td></td></tr><tr><td><img src="/files/z0QM8zchJVkiEbMBkiQ3" alt=""></td><td><code>{</code>authAction: { login: false, signup: true },<code>loginProvider: ['google', 'email_password'],</code>withModal: true<code>}</code></td><td></td></tr><tr><td><img src="/files/QjXS8hUj11aMwuc2FqIA" alt=""></td><td><code>{</code>authAction: { login: true, signup: falsse },<code>loginProvider: ['google', 'email_password'],</code>withModal: true<code>}</code></td><td></td></tr><tr><td><img src="/files/DvIujKaOFfSdM7NTcZWv" alt=""></td><td><code>{customUI: { words: { ja: { modal: { termsConditions: 'アカウント登録に伴い、Startbahn Port のプライバシーポリシーを含む、利用規約に同意します', termsConditionsLinkUrl: '</code><a href="https://google.com/"><code>https://google.com</code></a><code>' } }, withModal: true,</code>authAction: { login: true, signup: false },<code>loginProvider: [ 'google']}</code></td><td></td></tr><tr><td><img src="/files/fXJDD1f98Xu9R6rlgPrB" alt=""></td><td>{<code>loginProvider: ['google', 'line', 'twitter', 'email_passwordless'], authAction: { login: true, signup: false }, withModal: true}</code></td><td></td></tr></tbody></table>


# Whitelabeling/Customizing

## Customize UI

* It's possible from the config values / SDK
* Here is the mapping between config values and the corresponding UI.

<figure><img src="/files/R47Ly6cVIB06XHcFQgZ2" alt=""><figcaption></figcaption></figure>

{% hint style="warning" %}
`Guideline for the logoUrl` and`logoWhiteUrl`

* Logo must be hosted. If it is difficult, please contact us.

  e.g.: <https://static-files.startrail.io/startrail-black-logo.png>
* Logo is recommended to be square, given it is rounded into a circular shape icon in the modal and loading window. However there is no limitation for its shape and size.
* Logo must be either in PNG or JPG/JPEG format. Some E-Mail clients do not support certain formats such as SVG.
  {% endhint %}

## Properties

All the properties listed below are *optional*.

## CustomUI

<table><thead><tr><th width="184">Parameters</th><th>Example</th><th>Description</th></tr></thead><tbody><tr><td>logoUrl</td><td><code>string</code></td><td>App logo to be shown on the light background (light theme)</td></tr><tr><td>logoWhiteUrl</td><td><code>string</code></td><td>App logo to be shown on the dark background (dark theme)</td></tr><tr><td>serviceName</td><td><code>string</code></td><td>Applied to all service name in Startrail-sdk-js</td></tr><tr><td>contactUrl</td><td><code>string</code></td><td>Applied to the contact email in signup Email</td></tr><tr><td>verificationEmailTitle</td><td><code>string</code></td><td>Applied to the title of the verification email when you select email_password</td></tr><tr><td>words</td><td><pre class="language-json"><code class="lang-json">{
  en: CustomWords
  ja: CustomWords
}
</code></pre></td><td>either <code>en</code> or <code>ja</code> can be used. The value of each is defined in <a href="#customwords"><em>CustomWords</em></a></td></tr></tbody></table>

***CustomWords*** is an object consisting of the following optional attributes. Each of the are explained subsequently.

```json5
{
    emailAuthPopup:EmailAuthPopup,
    modal:WalletModal,
    signPopup:SignPopup,
    embed:Embed
}
```

### EmailAuthPopup

<table><thead><tr><th>Parameters</th><th>Example</th><th data-hidden>Example</th></tr></thead><tbody><tr><td>titleLogin</td><td></td><td><code>string</code></td></tr><tr><td>titleSignup</td><td></td><td><code>string</code></td></tr></tbody></table>

### WalletModal

<table><thead><tr><th width="379">Parameters</th><th>Example</th><th data-hidden>Type</th></tr></thead><tbody><tr><td>continueLogin</td><td></td><td><code>string</code></td></tr><tr><td>termsConditions</td><td></td><td><code>string</code></td></tr><tr><td>termsConditionisLinkUrl</td><td></td><td><code>string</code></td></tr><tr><td>privacyPolicyLinkUrl</td><td></td><td><code>string</code></td></tr></tbody></table>

### SignPopup

<table><thead><tr><th>Parameters</th><th>Example</th><th data-hidden>Type</th><th data-hidden>Mandatory<select><option value="0b53396cce3a44bbb3fc6b9705449928" label="Optional" color="blue"></option></select></th></tr></thead><tbody><tr><td>title</td><td></td><td><code>string</code></td><td><span data-option="0b53396cce3a44bbb3fc6b9705449928">Optional</span></td></tr><tr><td>requestFrom</td><td></td><td><code>string</code></td><td><span data-option="0b53396cce3a44bbb3fc6b9705449928">Optional</span></td></tr><tr><td>confirm</td><td></td><td><code>string</code></td><td><span data-option="0b53396cce3a44bbb3fc6b9705449928">Optional</span></td></tr><tr><td>cancel</td><td></td><td><code>string</code></td><td><span data-option="0b53396cce3a44bbb3fc6b9705449928">Optional</span></td></tr></tbody></table>

### Embed (Continue Alert Popup)

<table><thead><tr><th>Parameters</th><th>Example</th><th data-hidden>Type</th><th data-hidden>Mandatory<select><option value="b57b13ab0ccd46c690fb88143436a318" label="Optional" color="blue"></option></select></th></tr></thead><tbody><tr><td>continue</td><td></td><td><code>string</code></td><td><span data-option="b57b13ab0ccd46c690fb88143436a318">Optional</span></td></tr><tr><td>actionRequired</td><td></td><td><code>string</code></td><td><span data-option="b57b13ab0ccd46c690fb88143436a318">Optional</span></td></tr><tr><td>pendingAction</td><td></td><td><code>string</code></td><td><span data-option="b57b13ab0ccd46c690fb88143436a318">Optional</span></td></tr></tbody></table>


# Email Password

## A popup upon Email Password / Email

<table data-card-size="large" data-view="cards"><thead><tr><th></th><th></th><th></th></tr></thead><tbody><tr><td><img src="/files/OsecJ0raUUpLGFFK39VD" alt=""></td><td></td><td></td></tr><tr><td><img src="/files/BmUs4800f3f9kdD9caM5" alt=""></td><td></td><td></td></tr></tbody></table>

## Email Verification

#### When used ?

* When signup is clicked by a user, an email is sent to the email address the user enters for authentication.

<figure><img src="/files/Kr7VPz05pnulFgpXsebO" alt=""><figcaption></figcaption></figure>

Please refer to [Getting Started](/startrail-sdk-js/getting-started) for more details of how to set callbackUrl

{% hint style="info" %}
Redirect Url that is generated at user's click contains the following query parameters.

`https://redirectUrl.com?disable_signup=true&auth_provider=email_password`

You can get the query to identify the status that user is returned from signup process.
{% endhint %}

### UI Samples with combination of parameters

<table data-view="cards"><thead><tr><th></th><th></th><th></th></tr></thead><tbody><tr><td><img src="/files/IH6MaoYlBUQNPJuqM4kE" alt=""></td><td><code>{loginProvider: ['email_password']}</code></td><td></td></tr><tr><td><img src="/files/eFnJ0K1U6m76qvjYzAMo" alt=""></td><td><code>{loginProvider: ['email_password'], authAction: { login: false, signup: true }}</code></td><td></td></tr><tr><td><img src="/files/qXfWZxWxKcbcbERGDdXs" alt=""></td><td><code>{loginProvider: ['email_password'] authAction: { login: true, signup: false }}</code></td><td></td></tr></tbody></table>


# Hints

Guides for the end users

Authentication Request from Web3Auth

Following login providers explicitly requests authentication consent from Web3Auth under Oauth2.0 scheme since Startrail-Sdk-Js makes use of Web3Auth service under the hood.

{% hint style="warning" %}
Please inform your clients to trust the following authenticators, e.g. `Web3Auth`, `openlogin.`
{% endhint %}

<table data-view="cards"><thead><tr><th></th><th></th><th></th></tr></thead><tbody><tr><td><img src="/files/Hv2l77s37BPS8Zygfwxz" alt=""></td><td>Facebook</td><td></td></tr><tr><td><img src="/files/sOmfeump1udm5mUIrLJm" alt=""></td><td>Apple</td><td></td></tr><tr><td><img src="/files/c4scznRkSarSaG7FSODD" alt=""></td><td>Twitter</td><td></td></tr><tr><td><img src="/files/JYmtghdOViMTZO7JM6Qu" alt=""></td><td>Email Passwordless</td><td></td></tr><tr><td><img src="/files/uc8ac3Sv1rDgypg4JXdX" alt=""></td><td>Google</td><td></td></tr></tbody></table>

## Continue Alert Popup

Continue popup is displayed on the top right side of the screen with the modal set false (`{withModal: false}`), when the popups are blocked by browser Clicking Continue allows users to proceed with normal login flow.

![](/files/GDdyDDSrdLQlL8jHSU1q)

You can also customize the wordings. See more details in [whitelabeling](/startrail-sdk-js/login-providers/whitelabeling) page.


# Multi Factor Account Management

New device detected

{% hint style="warning" %}
The `mfaLevel` constructor option is **deprecated** as of **v2.2.0** and has no effect — the underlying Torus-embed v6 (ws-embed) API no longer exposes MFA-level configuration. MFA is still managed by the Web3Auth wallet itself, as described below; this page is unaffected. Code that passes `mfaLevel` keeps working, but the value is ignored.
{% endhint %}

<figure><img src="/files/rF3loeGWYzR6E3OkJ3cW" alt=""><figcaption></figcaption></figure>

`New device detected` modal could be opened for those who had signed in with multi factor authentication in Web3Auth wallet.

Here are the potential solutions.

1. You should have received an email with the subject "Your Web3Auth backup phrase" which contains a backup passphrase. Please check that email, copy the passphrase, and paste it into the field on the corresponding page.
2. After pressing the "Verify with other factors" button in the attached image and returning to the screen, other recovery methods should be presented. Please use any one of those methods.
3. Log in to the Openlogin login screen (<https://app.openlogin.com/>), press the "Manage Account" button on the dashboard, and obtain the recovery phrase. Please paste the recovery phrase after obtaining it.

<figure><img src="/files/EM7zreZVlV3nXF0HMj2Q" alt=""><figcaption></figcaption></figure>


# MetaMask

{% hint style="success" %}
MetaMask extension on desktop browser or MetaMask native app on mobile must be installed in the environment of an enduser in advance.
{% endhint %}

{% hint style="info" %}
It’s strongly recommend to check that currently active EOA in MetaMask is equal to the one user logged in or signed up with before requesting signing action because MetaMask are always open to users’ action regardless of Startrail-sdk-js state. ie. It is possible for users to change the EOA account on MetaMask whenever they like.
{% endhint %}

### For Mobile Users

Users are directed to the internal browser within the MetaMask app if they already have the native app installed; otherwise, they are redirected to the app store page.

{% hint style="info" %}
When users attempt to connect to the MetaMask browser app using the login function, irrespective of whether the native browser app is installed or not, a "false" value is returned. Please handle this scenario appropriately on the client side.
{% endhint %}

## UI

### \[Popup] Connect with MetaMask

#### Condition to happen

For the first time user connects to the web-application(URL) with MetaMask.

#### Impact

Popup request opens up for users.

#### When to happen in your web-application

When users login to Metamask.

<table data-card-size="large" data-view="cards"><thead><tr><th></th><th></th><th></th></tr></thead><tbody><tr><td><img src="/files/Mu6Ue7SUuxqjq6JxQTdx" alt=""></td><td></td><td></td></tr><tr><td><img src="/files/AWBmQVebcNn7Bszp1hCr" alt=""></td><td></td><td></td></tr></tbody></table>

### \[Popup] Allow this site to switch the network ?

#### Condition to happen

For the first time user connects Metamask to Polygon or Amoy network.

#### Impact

Popup request opens up for users.

#### When to happen in your web-application

Whenever uses sign for blockchain tx such as create SRR or transfer ownership.

<table data-view="cards"><thead><tr><th></th><th></th><th></th></tr></thead><tbody><tr><td><img src="/files/fmnDuQCYHcbh5nMXvC12" alt=""></td><td></td><td></td></tr><tr><td><img src="/files/8w0QBtsxijSxTI6HqvIY" alt=""></td><td></td><td></td></tr><tr><td><img src="/files/zshvGp1grQTQ0aLabHVs" alt=""></td><td></td><td></td></tr></tbody></table>

### \[Popup] Signature Request

#### Condition to happen

`sdk.signMessage` is invoked / `sdk.signMessage`

#### Impact / 影響

Popup request opens up for users

#### When to happen in your web-application

When user signs the message `sdk.signMessage()`

{% hint style="danger" %}
Login to your application with signing requires Popup confirmation to users unlike the case with Startrail login
{% endhint %}

<figure><img src="/files/FFMhf7PSrv1YigX8Zuzj" alt=""><figcaption></figcaption></figure>

### Network Error

When network error occurs, please go to Settings > Networks on MetaMask, select active network, and change **New RPC URL** to the live one selected from the list below.

[RPC endpoint and chainId](/startrail-sdk-js/getting-started/rpc-endpoint)

{% hint style="danger" %}
This Network error is more likely to occur in Test environment Amoy.
{% endhint %}

<figure><img src="/files/ixSI5mxqqJLHgtCDlckd" alt=""><figcaption></figcaption></figure>


# Authentication Integration

To integrate user authentication with your backend system

## Authentication Integration Flow <a href="#f822c50b-f33d-48c3-bb35-edab40bfaf74" id="f822c50b-f33d-48c3-bb35-edab40bfaf74"></a>

Some of the key features include:

* Assign an EOA to an enduser
* Authenticate the user in backend

<figure><img src="/files/nIdqm8EWxATW1QAYohnL" alt=""><figcaption></figcaption></figure>

{% hint style="warning" %}

#### Additional development is required as follows

{% endhint %}

#### Outline

If you are using Ethereum Signature Validator for verification, you need to restore the message that was used for the signature on the backend. Please add the following code to your backend. The reason for not sending the restored message directly from the frontend is to ensure that the message generated on the backend is revealed to be used for the signature. Without this assurance, there is a possibility of picking up any string and signature and impersonating someone else.

#### Summary

{% hint style="info" %}

#### Implementation Details of above Flow at No9 <a href="#a9e7ac77-e54a-461d-8f4a-15d9089c5123" id="a9e7ac77-e54a-461d-8f4a-15d9089c5123"></a>

{% endhint %}

1. Refetch the originalMessage
2. Add the logic to generate messageToBeSigned from originalMessage

```
// ① Fetch the originally generated string that was passed to the user
const originalMessage = await this.findOne(eoa)

let messageToBeSigned
if (prefix) {
	// ② Restore the messageToBeSigned, from the originalMessage, that was signed inside Startrail-sdk-js.
  messageToBeSigned = `${prefix}${originalMessage.length.toString()}${originalMessage}`;
} else {
	// Write the logic in case the prefix is undefined given the usecase that popup is not hidden
  messageToBeSigned = originalMessage
}

// HTTP call to Validator-API (message: messageToBeSigned, signature, address )
```


# Errors

To know the error response from Startrail-Sdk-Js

## Description

Errors inheriting from `Error` objects will be thrown, and custom error properties are described in the `Response` below

See more details for [Error objects](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error)

## Response

| Variable    | Type             | Description                                   |
| ----------- | ---------------- | --------------------------------------------- |
| `from`      | `"StartrailSdk"` | To identify where the error comes from        |
| `errorCode` | `ErrorCode`      | To identify the error type for error handling |

## ErrorCode

Followings are the errors to be returned. Frontend can catch and handle it accordingly for better UX.

<details>

<summary>AUTH0_VERIFY_EMAIL</summary>

**What:**

Immediately after signup is submitted with Email Password by a user, this error is thrown.

**Action to take:**

You can customize the UI/UX to align with your project's specific plans and requirements. How you choose to handle this customization depends on the unique needs of your project.

</details>

<details>

<summary>METADATA_VALIDATION_FAILED</summary>

**What:**

Metadata validation fails duet to schema validation.

**Action to take:**

Revise the metadata object itself based on the error message.

</details>

<details>

<summary>STARTRAIL_API_ERROR</summary>

**What:**

Error occurs in StartrailAPI.

**Action to take:**

Check error message to identify the content.

</details>

<details>

<summary>TORUS_USER_REJECT_WALLET_REQUEST</summary>

**What:**

The user closes the confirmation popup presented for signing.

**Action to take:**

You can customize the UI/UX to align with your project's specific plans and requirements. How you choose to handle this customization depends on the unique needs of your project.

</details>

<details>

<summary>WALLET_NOT_INITIALIZED</summary>

**What:**

After logging out from the SDK, the wallet instance becomes undefined.

**Action to take:**

Call `new Startrail()` once again in your web-application.

</details>

<details>

<summary>WALLET_NOT_SUPPORTED</summary>

**What:**

Unsupported wallet is called. (Official MetaMask library calls Coinbase Wallet browser extension when it is activated)

**Action to take:**

Request end-users to switch your wallet.

</details>

<details>

<summary>WALLET_EOA_NOT_MATCH</summary>

**What:**

It is not possible to prevent end-users from switching their EOA on Metamask even after logging in, which can potentially lead to a scenario where the EOA used for signing, e.g., for issuing SRR, is different from the one they initially logged into the web application.

**When**

When SDK calls StartrailAPI

**Action to take:**

Request to switch back to the EOA with which the user originally logged in.

</details>

<details>

<summary>WALLET_NOT_FOUND</summary>

**What:**

The wallet does not open because it has not been activated (unlocked) in your browser extension.

**Action to take:**

Request end-users to activate or unlock your wallet.

</details>

<details>

<summary>WALLET_NOT_SUPPORT_FUNCTION</summary>

**What:**

Some functions are not supported for particular wallets. eg. `overwriteConfig()` or `switchLanguage()` are not supported in Metamask.

**Action to take:**

It depends on frontend developers

</details>

## Response Example

###

Example

```
{
	from: 'StartrailSdk',
	errorCode: 'TORUS_USER_REJECT_WALLET_REQUEST',
	message: 'Torus Message Signature: User denied message signature.',

}
```


# Change logs

Summary of the changes, updates, and fixes made to the Startrail-Sdk-Js

#### Available Versions

* [v2.0.4 \[recommended\]](/startrail-sdk-js/change-logs/v2.0.4)
* [v2.0.1](/startrail-sdk-js/change-logs/v2.0.1)

For detailed information on each version, visit their respective pages linked above.

{% hint style="danger" %}
Due to the shutdown of the legacy `Torus-embed` service by Web3Auth, all versions of this SDK below v2.0.0 are now deprecated and will soon be unsupported, 28th of Feb 2026. To maintain service continuity, all integrators must upgrade to the latest version immediately.
{% endhint %}


# v2.2.0

## [npmjs](https://www.npmjs.com/package/@startbahn/startrail-sdk-js/v/2.2.0)

{% hint style="success" %}
**Highlight —** a single `email_passwordless` provider with a known email (`loginHint`) now logs the user in **without opening the selection modal**.
{% endhint %}

## ✨ Changes

* **Email Passwordless without a modal.** When a single `email_passwordless` login provider is configured together with a `loginHint` (the user's email, known in advance), `login()` now starts the passwordless flow directly and **skips the login-provider selection modal**.
  * New `loginHint` option, accepted both on the constructor config and on the per-call `login()` override.
  * If `loginHint` is missing or not a valid email, the modal opens as before — so the behaviour is safe to adopt incrementally.
* **Quieter by default — new `debug` flag.** The SDK no longer prints verbose logs to the browser console. Pass `debug: true` in the constructor config to re-enable detailed logging while developing.
* **`mfaLevel` is deprecated.** It has no effect since Torus-embed v6 (the underlying ws-embed API has no MFA-level support). Existing code that passes `mfaLevel` keeps working, but the option is ignored.

{% code title="Skip the modal with loginHint" overflow="wrap" %}

```typescript
const sdk = new Startrail({
  env: 'staging',
  loginProvider: ['email_passwordless'],
  loginHint: 'user@example.com', // known email → passwordless starts directly, no modal
})
await sdk.login()
```

{% endcode %}

{% content-ref url="<https://github.com/startbahn/api-portal/blob/main/startrail-sdk-js/initialization.md>" %}
<https://github.com/startbahn/api-portal/blob/main/startrail-sdk-js/initialization.md>
{% endcontent-ref %}

## 🤖 Agent & LLM friendly

This release ships first-class resources so AI coding tools (Claude, Cursor, Copilot, …) integrate the SDK correctly with minimal context.

<table data-view="cards"><thead><tr><th></th><th></th><th></th></tr></thead><tbody><tr><td><strong>📄 llms.txt</strong></td><td>Machine-readable API reference — install, lifecycle, every method signature, the environment table and the error catalogue.</td><td><strong>Bundled in the npm package</strong><br><code>node_modules/@startbahn/startrail-sdk-js/llms.txt</code></td></tr><tr><td><strong>🤖 AGENTS.md</strong></td><td>Rules + the canonical <code>construct → login → action</code> pattern for agents writing code against the SDK.</td><td>In the SDK source repository</td></tr><tr><td><strong>💬 Typed TSDoc</strong></td><td>Rich hover docs and autocomplete on the public class and request/response types, straight from your editor.</td><td>Bundled in the package types</td></tr></tbody></table>

{% hint style="info" %}
Point your AI assistant at `node_modules/@startbahn/startrail-sdk-js/llms.txt` for an accurate, version-matched reference instead of relying on its training data.
{% endhint %}

## 🛠️ Internal

* Migrated the build/release toolchain from Yarn to **pnpm 11 (Node 22)** and refreshed the lint/build config. No change to how you install or consume the package.
* Refactored the Torus login flow (more reliable early-EOA return) and resolved outstanding Dependabot security alerts.

{% content-ref url="/pages/frSJRNrPXDXGs0OwRaIS" %}
[URL per environment](/readme/url-per-environment)
{% endcontent-ref %}


# v2.0.4

## [npmjs](https://www.npmjs.com/package/@startbahn/startrail-sdk-js/v/2.0.4)

## Changes

* Improves signing mechanism
  * Encourages `personal_sign` over deprecated `eth_sign`.
* Add white labels, eg. logo, language and etc.

{% content-ref url="/pages/frSJRNrPXDXGs0OwRaIS" %}
[URL per environment](/readme/url-per-environment)
{% endcontent-ref %}


# v2.0.2

## [npmjs](https://www.npmjs.com/package/@startbahn/startrail-sdk-js/v/2.0.2)

## Changes

* For staging environment is using Torus production build over testnet.
  * This might reduce the chance of hitting 403 in staging
* Developing modal fully in house, without relying on Bootsrap. This allows using shadow DOM to avoid any possible side effect of the css.

{% content-ref url="/pages/frSJRNrPXDXGs0OwRaIS" %}
[URL per environment](/readme/url-per-environment)
{% endcontent-ref %}


# v2.0.1

{% hint style="danger" %}
Due to the shutdown of the legacy `Torus-embed` service by Web3Auth, all versions of this SDK below v2.0.0 are now deprecated and will soon be unsupported, 28th of Feb 2026. To maintain service continuity, all integrators must upgrade to the latest version immediately.
{% endhint %}

## Changes

* Upgraded to [Torus-embed V6.2.0](https://www.npmjs.com/package/@toruslabs/torus-embed/v/6.2.0)
* Implemented a custom modal component, rather than the Torus default modal, using dynamic Bootstrap injection. The modal now programmatically injects the necessary header scripts and styles into the DOM.
  * This allows users to hand pick any combination of the login methods.
* Several UI customization may not be applied the same as before due to change of the underlying SDK.

{% content-ref url="/pages/frSJRNrPXDXGs0OwRaIS" %}
[URL per environment](/readme/url-per-environment)
{% endcontent-ref %}


# v1.36.0

All the older versions are deprecated as they deemed unusable given the deprecation of the Web3Auth dependency

{% hint style="danger" %}
Due to the shutdown of the legacy `Torus-embed` service by Web3Auth, all versions of this SDK below v2.0.0 are now deprecated and will soon be unsupported, 28th of Feb 2026. To maintain service continuity, all integrators must upgrade to the latest version immediately.
{% endhint %}

## Changes

* Upgrade `@toruslabs/torus-embed` to v5.0.2 \[[ref](https://www.npmjs.com/package/@toruslabs/torus-embed/v/5.0.2)]
* Minor/Patch upgrades to the rest of the packages


# v1.35.0

{% hint style="info" %}
For SDK to function in testing environment, please use this version onward.
{% endhint %}

## Changes

* Updated the default network configuration from Mumbai to Amoy on the staging environment following the deprecation of the Mumbai network.
* Introduced a new parameter chainId, during the instantiation of StartrailSdk. For more details, refer to [RPC endpoint and chainId](/startrail-sdk-js/getting-started/rpc-endpoint)
* Fixed the issue where the process object is undefined when SDK is directly imported via CDN, which occurred after the release of v1.34.0.

{% content-ref url="/pages/frSJRNrPXDXGs0OwRaIS" %}
[URL per environment](/readme/url-per-environment)
{% endcontent-ref %}


# v1.34.0

## Changes

* Supported collection for [bulk SRR creation and transfer](/startrail-sdk-js/startrail-api-methods/bulk)
* The function `transferFromWithProvenance` is deprecated and has been renamed to `transferSRRToEthereumAddress` for better intuitive understanding. See more [details](/startrail-sdk-js/startrail-api-methods/transfersrrtoethereumaddress).
* The argument `isHashPreimageEnabled` in transfer-related functions has been deprecated.
  * The following are the functions on impact.
    * [Approve SRR By Commitment](/startrail-sdk-js/startrail-api-methods/approvesrrbycommitment)
    * [Bulk](/startrail-sdk-js/startrail-api-methods/bulk)
* The package bundle size has been reduced by approximately 50%.
* The version of torus-embed has been updated to v4.1.3.
* A potential bug has been addressed where the new `$schema` URL in transfer metadata would be overwritten during the execution of transfer functions when a new version is released in the future.
* `ethereum.send()` has been replaced with `ethereum.request()` in the Ethereum provider due to its deprecation. (No impact on clients)


# v1.33.2

## Changes

* Implemented a patch to ensure that "email\_password" is returned as the value of `typeOfLogin` in the `getUserInfo()` response when "email\_password" is provided as a parameter of the `loginProvider`.

### Background

* The Web3Auth library underwent an unexpected specification change, altering the `typeOfLogin` value returned from `getUserInfo()`. In PROD, it changed from "email\_password" to "startrail-auth0-email-password," and in staging, it changed to "startrail-auth0-staging."

### Impact

* Clients relying on Email Password and the `typeOfLogin` value in their system may be affected. The extent of the impact varies depending on the specific implementation in each application.


# v1.33.1

Adding Parameter for MFA flexibility according to the following feature by Torus/web3auth

To deactivate 2FA modal window, pls update Startaril-sdk-js version to `v1.33.1 >=`

<figure><img src="/files/RgFZuvWEsKVNCNojGPi7" alt=""><figcaption></figcaption></figure>

If you would like to update the settings for the modal, please refer to the document below and [Getting Started](/startrail-sdk-js/getting-started)

{% embed url="<https://web3auth.io/docs/sdk/pnp/unreal/mfa#mfalevel>" %}


# v1.32.0

{% hint style="warning" %}
Please use [v1.30.6](/startrail-sdk-js/change-logs/v1.30.6) instead
{% endhint %}

<details>

<summary>New Change</summary>

Wording of Continue Alert Popup is customiable now.

See more details below

<img src="/files/2UzHJ7yhYYqbCP059W6q" alt="" data-size="original">

</details>


# v1.31.1

@July 14, 2023

{% hint style="warning" %}
Please use [v1.30.6](/startrail-sdk-js/change-logs/v1.30.6) instead
{% endhint %}

<details>

<summary>New Change</summary>

Social logins are added.

It is a major upgrade to the SDK that enables authentication via the following new verifiers:

* LINE
* Apple
* Facebook
* Twitter
* Passwordless login via Email

</details>


# v1.30.6

### New Change

* Include the MetaMask library in the babel transpile for resolving build issues in Webpack version 4.

### What you need to do

* Please make sure your application runs on at least Node 18 since the minimum required Node version has been raised to 18 following the torus-embed update,

See more details in [v1.30.5](/startrail-sdk-js/change-logs/v1.30.5)


# v1.30.5

### New Change

* Bump up torus-embed version to v4.0.0 that solves the issue that the tab for Email Password signup remained unclosed.
* Raise the minimum required Node version to 18 following the torus-embed update.

### What you need to do

* Please make sure your application runs on at least Node 18.


# v1.30.4

### New Change

* Enable the buffer module in the browser through Webpack configuration.


# v1.30.3

### New Change

* Solved the build issue on Webpack v4


# v1.30.2

{% hint style="danger" %}
From this change, node version requires >= 16.18.1
{% endhint %}

## New Changes

* Updated torus-embed version to `@toruslabs/torus-embed": "^2.2.6` that solves Google Chrome v116 issue.

### This chnge will sove following issues

* Google Chrome v116 issue. Login does not work with Chrome version above v116.
* On STG environment. chainId issue occured for any call to Startrail-API.

### This change will solve following issues

* Google Chrome v116 issue. Login does not work with Chrome version above v116.
* On STG environment. chainId issue occurred for any call to Startrail-API.


# v1.30.1

@April 5, 2023

### What is updated?

Add new checkERC2981Royalty function to get the royalty state from SRR


# v1.30.0

@April 4, 2023

### What is updated?

* Update circle CI config for internal use
* Bump up "@toruslabs/torus-embed": "1.38.7"
* Add "@ethersproject/abstract-signer": "^5.7.0",
* Bump up npm packages


# v1.29.1

@October 18, 2022

### What is updated?

Fix the bug that `bulk` function did not hash the `preimage` argument even though the value `isHashPreimageEnabled: true` is passed


# v1.29.0

@October 11, 2022

### What is updated?

* Now MetaMask is available on mobile.

詳しくはこちらをご確認ください [Metamask](/startrail-sdk-js/metamask)

* Now string message is available to be passed with the argument in sdk.signMessage(’string message’)

  Example

<figure><img src="/files/FFMhf7PSrv1YigX8Zuzj" alt=""><figcaption></figcaption></figure>


# v1.28.2

@September 9, 2022

### What is updated?

* Bump up torus-embed version to 1.35.5, which resolved logout popup appearance issue at re-login timing as attached image.

<figure><img src="/files/RCkKxF5t8YB7DCy6VNFQ" alt=""><figcaption></figcaption></figure>


# v1.28.1

@August 25, 2022

### What is updated?

* Revert the change that invoked torusEmbed init() function at new Startrail() timing. The motivation behind is to make the code more concise to handle the case that it was hard to get the state by torusEmbed().init() in constructor which is called only synchronously when new Startrail() and sdk.login() were invoked in a single user action.


# v1.28.0

@August 16, 2022

### What is updated?

* Fix the bug that other wallet extensions than MetaMask got activated when multiple Web3.0 wallet extensions are activated on your browser.
* RPC endpoints in the list are checked for liveness inside StartrailSdk in advance and automatically sets the live one.
* Signing logic with MetaMask is switched to personal\_sign which allows removing warning messages on the Signature request popup.

<figure><img src="/files/iC9LdU6dpQYJLG0NcKfM" alt=""><figcaption></figcaption></figure>

* Logo url for the white colour is now available to be set which is used in transition popup by Email & Password login. New property is logoWhiteUrl.

<figure><img src="/files/2q04ZYfbpBaBIbMi4Zp5" alt=""><figcaption></figcaption></figure>

* Add `METADATA_VALIDATION_FAILED` errorCode for the case that blockchain tx with metadata validation fails.
* Changed to invoke torusEmbed init() function at new Startrail() timing.


# v1.27.1

@May 9, 2022

### What is updated?

* Change default RPC endpoint for lrc/staging environment to [https://rpc-mumbai.matic.today](https://rpc-mumbai.matic.today/)to solve the error with the previously set endpoint URL: [https://rpc-mumbai.maticvigil.com](https://rpc-mumbai.maticvigil.com/)


# v1.27.0

@April 11, 2022

### What is updated?

* RPC endpoint is now changeable from init timing

```
new Startrail({
	rpcEndpoint: string
})
```

* Reduce the Startrail-sdk-js size from **8.34 MB** to **5.59 MB**
* Convert unchecksum case EOA value to checksum for issue and transfer SRRs


# v1.26.0

@March 29, 2022

### What is updated?

* isNewUser property is added by torusEmbed in getUserInfo() response.

```
export interface UserInfo {
  /**
   * Returns if the logged in user is new to Torus wallet which supports Startrail login
   */
  isNewUser?: boolean | ''
  /**
   * Email of the logged in user if your selected wallets knows it
   */
  email: string
  /**
   * Full name of the logged in user if your selected wallets knows it
   */
  name: string
  /**
   * Profile image of the logged in user if your selected wallets knows it
   */
  profileImage: string
  /**
   * verifier of the logged in user (google, facebook etc) if your selected wallets knows it
   */
  verifier: string
  /**
   * Verifier Id of the logged in user if your selected wallets knows it
   *
   * email for google,
   * id for facebook,
   * username for reddit,
   * id for twitch,
   * id for discord
   */
  verifierId: string
  typeOfLogin?: LoginProvider | ''
  wallet?: WalletType
}
```


# v1.25.2(Security Patch)

@February 24, 2022

### 概要

[API仕様書](https://www.notion.so/Startrail-PORT-All-in-one-document-for-API-SDK-API-SDK-36c4f8078d494919866284ead6d5d9e5?pvs=21)に記載のSignup/Login Flowに関して、お伝えしていたフローの署名検証に問題がありました。なんらかの方法で他者のEOAの電子署名を取得でき、かつStartrailの仕組みを高い精度で理解している人に限り、その他者のEOAになりすますことが可能なことが分かりました。

### Startrail-sdk-jsのパッチ・バージョン：`1.25.2`

### フロー上で必要な変更(色線が変更箇所)

<table data-card-size="large" data-view="cards"><thead><tr><th></th><th></th><th></th></tr></thead><tbody><tr><td>Before</td><td><img src="/files/n0x5kglUyEYbeY8BCmZX" alt=""></td><td></td></tr><tr><td>After</td><td><img src="/files/x5xiCYH0Pt7lQ5TBxiHM" alt=""></td><td></td></tr></tbody></table>

```
# 差分のまとめ(番号はAfter図参照)
20.messageからprefixに変更
21.メッセージから接頭辞(=prefix)に変更
(Before:22.は削除)
24. 文字列から接頭文字に変更
25. 文字列から接頭文字を足し合わせた文字列に変更
```

### セキュリティ脆弱性について

Signup/Login Flow図の中で以下のフローがありますが、

<figure><img src="/files/FW31ATLwhXgOW9lJVpHI" alt=""><figcaption></figcaption></figure>

```
18. ランダムな文字列(=message)を生成し、EOAとマッピングさせる
22. カスタマイズしたメッセージ(=messageToBeSigned)を文字列として置き換える
26. 文字列、ユーザ情報、シグネチャを送信
27. 文字列、シグネチャからEOAを復元
```

22でカスタマイズしたメッセージ(=messageToBeSigned)を文字列に置き換えてサーバサイドに送信するため、サーバ側では18で生成したランダムな文字列(=message)と27で復元に使う文字列が同等の値であることを判断することは、22で使うカスタマイズのロジックを共有していないことから、困難だと言えます。

このことは、ユーザが任意のmessageToBeSigned、signatureを取得して、26のエンドポイントにリクエストを投げることで、27で他者のEOAを復元し、バリデーションを通過させてしまう可能性につながる。

#### メッセージのカスタマイズについて

Startrail-sdk-js内部では、署名を行う前に下記の処理を実行し、メッセージを変換しております。

理由は、ウォレットとして利用しているTorus社の実装に準拠したものであり、これによりメッセージに署名を行う際、ユーザはポップアップの確認ボタンを押す必要が無くなり、UIUXの改善につながります。

### コード上で必要な変更点

1. クライアント様のコード内にバリデーション用のロジックを追加する

   影響範囲と内容

   1. Startrail-sdk-jsの更新後、

      `signMessage()`のレスポンス値が

      ```jsx
      // 現状
      export interface SignMessage {
        signature: string
        messageHash: any
        message: string
      }
      // 新たな仕様
      export interface MessageSignature {
        signature: string
        prefix: string | undefined
      }
      ```

      上記のように変更されています。

      `messageHash`と`message`を送信する替わりに`prefix`をサーバサイドに送信してください。
   2. クライアント様側のサーバサイドに下記のようなロジックを追加いただきたいです

      要点:

      1. originalMessageを再取得する
      2. originalMessageからmessageToBeSignedを生成するロジックを追加

      ```jsx
      // ① DBからEOAを元に最初に生成し文字列を取り出す
      const originalMessage = await this.findOne(eoa)

      let messageToBeSigned
      if (prefix) {
      	// ② DBから取得したoriginalMessageを基に実際に署名を行った文字列を復元する
        messageToBeSigned = `${prefix}${originalMessage.length.toString()}${originalMessage}`;
      } else {
      　// ポップアップを隠さない場合も考慮し、prefixがundefinedである場合のロジックも記述
        messageToBeSigned = originalMessage
      }

      // HTTP call to Validator-API (message: messageToBeSigned, signature, address )
      ```

   #### 上記変更が難しい場合の代替案

   以下の動画にあるような追加のポップアップをログイン時に許容していただける場合は、messageToBeSignedの生成ロジックは必要なく、お使いのStartrail-sdk-jsでもご対応が可能となりますので、その際は別途、詳細を共有させてください。

{% file src="/files/NWgvmujeg4qWoH4W9tgb" %}


# Transfer SRR Ownership By RevealHash

Executes blockchain transaction to transfer an ownership of an SRR to another Ethereum address.

### Transfer With Default Collection

<mark style="color:green;">`POST`</mark> `<base_url>/startrail/api/v1/srr/{tokenId}/transferByReveal`

### Transfer With Custom Collection

<mark style="color:green;">`POST`</mark> `<base_url>/startrail/api/v1/srr/{contractAddress}/{tokenId}/transferByReveal`

Please replace `<base_url>` as explained [here](/readme/url-per-environment).

Transfer SRR to a new owner providing a reveal hash, the hash of matches the commitment given in the approval stage.

{% hint style="info" %}
Default collection address is not supported in `Transfer With Custom Collection`. Please use `Transfer With Default Collection` instead.
{% endhint %}

#### Path Parameters

| Name                                      | Type   | Description                        |
| ----------------------------------------- | ------ | ---------------------------------- |
| tokenId<mark style="color:red;">\*</mark> | String | Startrail Registry Record Token ID |
| contractAddress                           | String | The address of collection contract |

#### Request Body

| Name                                         | Type    | Description                                                                                         |
| -------------------------------------------- | ------- | --------------------------------------------------------------------------------------------------- |
| revealHash<mark style="color:red;">\*</mark> | String  | The key generated on Port Dashboard in advance for future transfer                                  |
| to<mark style="color:red;">\*</mark>         | String  | Next owner's EOA address. Both lowercase and mixed case which is compatible with EIP55 are accepted |
| isIntermediary                               | Boolean |                                                                                                     |

{% tabs %}
{% tab title="201: Created " %}

```json
{
  "txReceiptId": 0
}
```

{% endtab %}

{% tab title="400: Bad Request " %}

<pre class="language-json"><code class="lang-json"><strong>{
</strong>    "statusCode":400,
<strong>    "message":"hash of revealHash does not match the SRR transferCommitment"
</strong>}
</code></pre>

{% endtab %}

{% tab title="400: Bad Request " %}

```json
{
    "statusCode":400,
    "message":"metadata is invalid. Check it against the metadata JSON schema. Details: should NOT have additional properties ({"additionalProperty":"unknownField"})."
}
```

{% endtab %}

{% tab title="404: Not Found " %}

```json
{
    "statusCode":404,
    "message":"Token 41052230 not found"
}
```

{% endtab %}

{% tab title="500: Internal Server Error Transaction Sending Errors - these errors occur after validation but before the transaction is sent to Ethereum." %}

```json
{
    "statusCode":500,
    "message":"API account is out of funds"
}
```

{% endtab %}

{% tab title="500: Internal Server Error Transaction Sending Errors - these errors occur after validation but before the transaction is sent to Ethereum." %}

```json
{
    "statusCode":500,
    "message":"failed to send transaction toEthereum - internal error"
}
```

{% endtab %}
{% endtabs %}

## Request Body Example

```json
{
  "revealHash": "0x335929a4e59b0860ec04c620c1284dace74c00f7eadaadce7a18d6deba6c544e",
  "to": "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE",
  "isIntermediary": false
}
```

## Swagger Endpoint (Test Environment)

[Swagger to test](https://api-stg.startrail.startbahn.jp/api/#/default/SRRController_transferByReveal)

## Required Permissions

* You need to have a Licensed User

{% hint style="info" %}
You must pass the `revealHash which you generated at` [`approveSRRByCommitment()`](/startrail-sdk-js/startrail-api-methods/approvesrrbycommitment) `via Startrail-Sdk-Js in order to complate transfer`
{% endhint %}


# Get Transaction Data

## Get transaction(tx) data endpoint

The following URL points to the test environment. Replace it with the production's url (`https://api.startrail.io/startrail`) as necessary.

{% openapi src="/files/67PSeDEtDDMW3Fl8otOp" path="/api/v1/tx" method="get" %}
[api-stg-startrail.json](https://3244648189-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FOu6aN3RW264zdJsOQMJ2%2Fuploads%2FyaCSxHAWrGk5xRNQdnKS%2Fapi-stg-startrail.json?alt=media\&token=ba84be5d-9079-41a7-9f6b-6ceaf8ffe906)
{% endopenapi %}

## Swagger Endpoint (Test Environment)

[Swagger to test](https://api-stg.startrail.startbahn.jp/api#/default/TxController_getTx)

## Required Permissions

No required permission. This endpoint is public as the information is also public.


# Get Metadata By tokenid

tokenid of ERC721 specification.

The following URL points to the test environment. Replace it with the production's url (`https://api.startrail.io/startrail`) as necessary.

Please note that there are two endpoints here:

1. [tokenid only](#get-metadata-by-tokenid-only-endpoint)
2. [tokenid + contract address](#get-metadata-by-tokenid-and-collection-contract-address-endpoint)

## Get metadata by tokenid only endpoint

{% openapi src="/files/67PSeDEtDDMW3Fl8otOp" path="/api/v1/srr/metadata/{tokenFile}" method="get" %}
[api-stg-startrail.json](https://3244648189-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FOu6aN3RW264zdJsOQMJ2%2Fuploads%2FyaCSxHAWrGk5xRNQdnKS%2Fapi-stg-startrail.json?alt=media\&token=ba84be5d-9079-41a7-9f6b-6ceaf8ffe906)
{% endopenapi %}

If the tokenid belongs to a collection contract you should use the [following endpoint](#get-metadata-by-tokenid-and-collection-contract-address-endpoint) that gets the `contractAddress` as well, otherwise use the [above endpoint.](#get-metadata-by-tokenid-only-endpoint)

## Get metadata by tokenid and collection contract address endpoint

{% openapi src="/files/67PSeDEtDDMW3Fl8otOp" path="/api/v1/srr/metadata/{contractAddress}/{tokenFile}" method="get" %}
[api-stg-startrail.json](https://3244648189-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FOu6aN3RW264zdJsOQMJ2%2Fuploads%2FyaCSxHAWrGk5xRNQdnKS%2Fapi-stg-startrail.json?alt=media\&token=ba84be5d-9079-41a7-9f6b-6ceaf8ffe906)
{% endopenapi %}

## Swagger Endpoint (Test Environment)

[Swagger to Get metadata JSON for a given token.](https://api-stg.startrail.startbahn.jp/api/#/default/SRRController_getMetadataJSONByTokenID)

[Swagger to Get collection metadata JSON for a given token.](https://api-stg.startrail.startbahn.jp/api/#/default/SRRController_getCollectionMetadataJSONByTokenID)

## Required Permissions

No required permission. This endpoint is public as the information is also public.


# A introduction of subgraph

The Graph is a decentralized protocol for indexing and querying blockchain data. The Graph makes it possible to query data that is difficult to query directly.

Startrail is aggregating information on our Blockchain using The Graph. The unique mechanisms specific to each application are referred to as subgraph.

The source code is published [here](https://github.com/startbahn/startrail-contracts/tree/main/subgraph).

There are two types of subgraphs,

1. Hosted service
2. Decentralized service

We are currently using `1.`, but we are in the process of transitioning to `2.` It has been announced that the service for `1.` will eventually be discontinued.

You can get various information by querying the subgraph. Please refer to our subgraph [here](https://api.goldsky.com/api/public/project_cmgzivx09001u5np2h5sr10rb/subgraphs/startrail-polygon/subgraph-v1.20.0/gn).

Also, for staging is [here](https://api.goldsky.com/api/public/project_cmgzivx09001u5np2h5sr10rb/subgraphs/startrail-amoy-staging/subgraph-v1.20.0.1-stg/gn).




---

[Next Page](/llms-full.txt/1)

