NAV Navbar
javascript

Introduction

Welcome to The Codex API documentation! Among other things, you can use our API to create Codex Records on the Ethereum Blockchain without the need for any direct blockchain interaction.

To use The Codex API, you will need to register an OAuth2 application with us. See Authentication for more information.

If you have any questions or suggestions for edits to this documentation, please visit the GitHub repository and open an issue and/or pull request.

API URLs

The Codex API is available in 3 environments that correspond to 3 different Ethereum networks, so the API URL will vary based on the environment you are developing against.

Environment Network URL Description
Development Ropsten http://ropsten-api.codexprotocol.com We frequently deploy (possibly breaking) changes here. It is not recommended to develop your application against this network unless you are looking to test the latest changes to the Codex Protocol.
Staging Rinkeby https://rinkeby-api.codexprotocol.com We only deploy stable builds here, prior to Mainnet. This is the recommended network to test your application before deploying to production.
Production Mainnet https://api.codexprotocol.com Changes only get deployed here after being validated in our development and staging environments. All Codex Records created here are immutably recorded on the blockchain and cannot be destroyed. Integration into Mainnet should only be done after ensuring your application runs smoothly in the staging environment.

Authentication

Registering an Application

Currently there is no admin interface built out for application developers, so the process for registering an application requires manual steps from both Codex and the application developer. To register an application, send us an email with the following information:

Property Description
name The name of the application. This will be shown in The Codex Viewer as a registered application, taking the place of your application's Ethereum address in the Codex Record’s provenance.
email The application developer’s email address. This will be used to communicate any breaking API changes or development updates.
webhookUrl For example, https://your-api.example.com/codex-webhook. Since blockchain transactions are asynchronous, this webhookUrl will be used to inform your application that an event has occurred, for example when your Codex Records have been created. See webhooks for details.

Access Tokens

Obtaining an Access Token

Before you can make API calls, you'll need an access token. See Get an Access Token for more details.

Access Token Expiration

Example response when an expired access token is provided:

{
  "error": {
    "code": 401,
    "message": "Invalid token: access token is invalid"
  },
  "result": null
}

For security purposes, access tokens will expire after a certain period of time. If you send a request and and receive a 401 Unauthorized error, you'll need to generate another access token and send the request again.

The following table shows the access token expiries for each environment:

Network Expiry
Ropsten 30 days after creation
Rinkeby 90 days after creation
Mainnet 90 days after creation

Making Authenticated Requests

Making an authenticated API call:

import request from 'request'

// retrieve a list of your application's Codex Records
const options = {
  url: 'https://rinkeby-api.codexprotocol.com/v1/client/records',
  method: 'get',
  json: true,

  headers: {
    Authorization: 'Bearer d49694e5a3459759cc7ac1741de246e184e51d6e',
  },
}

request(options, (error, response) => {
  console.log(response.body.result) // an array of Codex Records
})

Remember to replace the example accessToken with your personal accessToken.

The Codex API expects an OAuth2 access token to be included with all requests made to the server in the Authorization header as follows:

Authorization: Bearer d49694e5a3459759cc7ac1741de246e184e51d6e

Public Routes

Unlike client routes, public routes do not require an accessToken in the Authorization header. However, sometimes an accessToken is required to retrieve a full response.

For example, to retrieve public information about a Codex Record, you could call GET /v1/record/:tokenId without an accessToken. If the Codex Record is public, then you will receive a full response. However, if the Codex Record is private, calling this route without an accessToken (or an accessToken that doesn't belong to the owner of the Codex Record) will return a response with all private information removed (i.e. no metadata).

Similarly, you will not be able to call public routes such as GET /v1/record/:tokenId/metadata if the record is private and the accessToken is missing (or if you provide your accessToken but you are not the owner.)

Get an Access Token

This endpoint can be used to generate an access token, which is required for making authenticated requests.

import request from 'request'

const options = {
  url: 'https://rinkeby-api.codexprotocol.com/v1/oauth2/token',
  method: 'post',
  json: true,

  // by using "form" here instead of "body", request will send this request as
  //  application/x-www-form-urlencoded
  form: {
    grant_type: 'client_credentials',
    client_id: '5bae56e05d7971becf854a70',
    client_secret: '352db118-b76c-4c60-9ed8-aa13cb4621b8',
  },
}

request(options, (error, response) => {
  console.log(response.body.result.accessToken) // also available is
})

The above API call returns an OAuth2 Access Token.

Remember to replace the example client_id and client_secret with your application's client_id and client_secret.

HTTP Request

GET /v1/oauth2/token

Request Parameters

Parameter Type Description
client_id String The unique identifier for your application, provided by Codex.
grant_type String Must be client_credentials.
client_secret String Your application's secret, provided by Codex.

Get a Codex Record

This endpoint can be used to retrieve a specific Codex Record. This will return a full Codex Record document, including it's metadata and provenance.

import request from 'request'

const options = {
  url: 'https://rinkeby-api.codexprotocol.com/v1/records/0',
  method: 'get',
  json: true,
}

request(options, (error, response) => {
  console.log(response.body.result) // Codex Record with tokenId 0
})

The above API call returns a single Codex Record.

HTTP Request

GET /v1/records/:tokenId

URL Parameters

Parameter Type Description
tokenId Number The tokenId of the Codex Record to retrieve.

Get a Codex Record's Metadata Only

This endpoint can be used to retrieve only the metadata of a specific Codex Record. This is useful if you do not need the entire Codex Record document.

import request from 'request'

const options = {
  url: 'https://rinkeby-api.codexprotocol.com/v1/records/0/metadata',
  method: 'get',
  json: true,
}

request(options, (error, response) => {
  console.log(response.body.result) // the metadata of Codex Record with tokenId 0
})

The above API call returns a Codex Record's metadata.

HTTP Request

GET /v1/records/:tokenId/metadata

URL Parameters

Parameter Type Description
tokenId Number The tokenId of the Codex Record for which to retrieve the metadata of.

Get a Codex Record's Provenance Only

This endpoint can be used to retrieve only the provenance of a specific Codex Record. This is useful if you do not need the entire Codex Record document.

import request from 'request'

const options = {
  url: 'https://rinkeby-api.codexprotocol.com/v1/records/0/provenance',
  method: 'get',
  json: true,

  // sort by date created
  body: {
    order: 'createdAt',
  },
}

request(options, (error, response) => {
  console.log(response.body.result) // the provenance of Codex Record with tokenId 0
})

The above API call returns a Codex Record's provenance.

HTTP Request

GET /v1/records/:tokenId/provenance

URL Parameters

Parameter Type Description
tokenId Number The tokenId of the Codex Record for which to retrieve the provenance of.

Request Parameters

Parameter Type Default Description
order String -createdAt To sort in chronological order, specify this value as createdAt.

Get a Codex Record's Main Image Only

This endpoint can be used to retrieve only the main image for a specific Codex Record. This is useful if you do not need the entire Codex Record document.

import request from 'request'

const options = {
  url: 'https://rinkeby-api.codexprotocol.com/v1/records/0/main-image',
  method: 'get',
  json: true,
}

request(options, (error, response) => {
  console.log(response.body.result) // the mainImage of Codex Record with tokenId 0
})

The above API call returns a single File.

HTTP Request

GET /v1/records/:tokenId/main-image

URL Parameters

Parameter Type Description
tokenId Number The tokenId of the Codex Record for which to retrieve the main image of.

Get a Codex Record's Images Only

This endpoint can be used to retrieve only the images array for a specific Codex Record. This is useful if you do not need the entire Codex Record document.

import request from 'request'

const options = {
  url: 'https://rinkeby-api.codexprotocol.com/v1/records/0/images',
  method: 'get',
  json: true,
}

request(options, (error, response) => {
  console.log(response.body.result) // the images array of Codex Record with tokenId 0
})

The above API call returns an array of Files.

HTTP Request

GET /v1/records/:tokenId/images

URL Parameters

Parameter Type Description
tokenId Number The tokenId of the Codex Record for which to retrieve the images array of.

Get a Codex Record's Files Only

This endpoint can be used to retrieve only the files array for a specific Codex Record. This is useful if you do not need the entire Codex Record document.

import request from 'request'

const options = {
  url: 'https://rinkeby-api.codexprotocol.com/v1/records/0/files',
  method: 'get',
  json: true,
}

request(options, (error, response) => {
  console.log(response.body.result) // the files array of Codex Record with tokenId 0
})

The above API call returns an array of Files.

HTTP Request

GET /v1/records/:tokenId/files

URL Parameters

Parameter Type Description
tokenId Number The tokenId of the Codex Record for which to retrieve the files array of.

Client Routes

Unlike public routes, client routes are all prefixed with /v1/client and only perform actions on data owned by your application. All client routes require an accessToken in the Authorization header, as described in the Access Tokens section.

Get Client

This endpoint can be used to retrieve the details of your application. This is useful if you want to check your CODX balance before sending a request that would consume CODX.

import request from 'request'

const options = {
  url: 'https://rinkeby-api.codexprotocol.com/v1/client',
  method: 'get',
  json: true,

  headers: {
    Authorization: 'Bearer d49694e5a3459759cc7ac1741de246e184e51d6e',
  },
}

request(options, (error, response) => {
  console.log(response.body.result) // your Client record
})

The above API call returns a single Client.

HTTP Request

GET /v1/client

Get All Codex Records

This endpoint can be used to retrieve all Codex Records that your application currently owns.

import request from 'request'

const options = {
  url: 'https://rinkeby-api.codexprotocol.com/v1/client/records',
  method: 'get',
  json: true,

  headers: {
    Authorization: 'Bearer d49694e5a3459759cc7ac1741de246e184e51d6e',
  },

  // sort by date created in reverse and get 5 records, skipping the first ten
  body: {
    limit: 5,
    offset: 10,
    order: '-createdAt',
  },
}

request(options, (error, response) => {
  console.log(response.body.result) // an array of your application's Codex Records
})

The above API call returns an array of Codex Records.

HTTP Request

GET /v1/client/records

Request Parameters

Parameter Type Default Description
limit Number 100 How many records to retrieve. Use with offset to paginate through Codex Records.
offset Number 0 How many records to skip before applying the limit. Use with limit to paginate through Codex Records.
order String createdAt To sort in reverse order, specify this value as -createdAt. Other options are metadata.name and -metadata.name.

Create a Codex Record

This endpoint can be used to begin the creation of a Codex Record. With this request, you're actually creating the Codex Record's metadata since the Codex Record itself can't be created until the transaction has been mined on the blockchain.

import fs from 'fs'
import request from 'request'

const options = {
  url: 'https://rinkeby-api.codexprotocol.com/v1/client/record',
  method: 'post',
  json: true,

  headers: {
    Authorization: 'Bearer d49694e5a3459759cc7ac1741de246e184e51d6e',
  },

  // by using "formData" here instead of "body", request will send this request
  //  as multipart/form-data, which is necessary for sending file data
  formData: {
    name: 'Really Cool Codex Record',
    description: 'This is a really cool Codex Record!',
    mainImage: fs.createReadStream('/tmp/uploads/cool-main-image.jpg'),
    images: [
      fs.createReadStream('/tmp/uploads/cool-supplemental-image-1.jpg'),
      fs.createReadStream('/tmp/uploads/cool-supplemental-image-2.jpg'),
    ],
    files: [
      fs.createReadStream('/tmp/uploads/author-notes.txt'),
      fs.createReadStream('/tmp/uploads/certificate-of-authenticity.pdf'),
    ],
    isPrivate: false,
    isHistoricalProvenancePrivate: true,
    whitelistedEmails: [
      'user@example.com',
    ],
    whitelistedAddresses: [
      '0xf17f52151ebef6c7334fad080c5704d77216b732',
      '0xc5fdf4076b8f3a5357c5e395ab970b5b54098fef',
      '0x821aea9a577a9b44299b9c15c88cf3087f3b5544',
      '0x0d1d4e623d10f9fba5db95830f7d3839406c6af2',
    ],
  },
}

request(options, (error, response) => {
  console.log(response.body.result) // the metadata for the soon-to-be-created Codex Record
})

The above API call returns Codex Record metadata, since the Codex Record itself can't be created until the transaction has been mined.

HTTP Request

POST /v1/client/record

Webhook Event

Event Name Recipient
codex-record:created Owner

Request Parameters

Parameter Type Description
name String The plain text name of this Codex Record.
description String (Optional) The plain text description of this Codex Record. The field can be set to null or simply omitted to leave the description empty.
mainImage File Data The main image of this Codex Record.
images Array[File Data] (Optional) An array of supplemental images that belong to this metadata.
files Array[File Data] (Optional) An array of supplemental files that belong to this metadata. This is considered the "historical provenance" of the Codex Record.
additionalMetadata Object (Optional) A list of additional metadata about this asset. See Additional Metadata for details.
auctionHouseMetadata Object (Optional) An arbitrary list of data specific to your application (this field is ignored if your application is not an auction house.) See Auction House Metadata for details.
isPrivate Boolean (Optional, default true) This flag indicates that the metadata for the Codex Record is private and can only be retrieved by the owner, the approvedAddress, and the addresses listed in whitelistedAddresses / whitelistedEmails.
isHistoricalProvenancePrivate Boolean (Optional, default true) This flag indicates whether or not historical provenance (i.e. files) should be hidden, regardless of the value of isPrivate.
whitelistedEmails Array[String] (Optional) An array of email addresses allowed to view private metadata for this Codex Record. This allows users to give people read-only access to their Codex Records.
whitelistedAddresses Array[String] (Optional) An array of Ethereum addresses allowed to view private metadata for this Codex Record. This allows users to give people read-only access to their Codex Records.

Modify a Codex Record

This endpoint can be used to modify the "off-chain" data for a Codex Record. Since no "on-chain" data can be modified with this request, the response will be the immediately-updated Codex Record.

import request from 'request'

const options = {
  url: 'https://rinkeby-api.codexprotocol.com/v1/client/record/0',
  method: 'put',
  json: true,

  headers: {
    Authorization: 'Bearer d49694e5a3459759cc7ac1741de246e184e51d6e',
  },

  body: {
    isPrivate: false,
    isHistoricalProvenancePrivate: false,
    whitelistedEmails: [
      'user@example.com',
    ],
    whitelistedAddresses: [
      '0xf17f52151ebef6c7334fad080c5704d77216b732',
      '0xc5fdf4076b8f3a5357c5e395ab970b5b54098fef',
      '0x821aea9a577a9b44299b9c15c88cf3087f3b5544',
      '0x0d1d4e623d10f9fba5db95830f7d3839406c6af2',
    ],
  },
}

request(options, (error, response) => {
  console.log(response.body.result) // the immediately-updated Codex Record
})

The above API call returns the immediately-updated Codex Record.

HTTP Request

PUT /v1/client/record/:tokenId

Webhook Event

Event Name Recipient
codex-record:address-whitelisted (if whitelistedAddresses and/or whitelistedEmails was replaced) Each newly added whitelisted user

URL Parameters

Parameter Type Description
tokenId Number The tokenId of the Codex Record to update.

Request Parameters

At least one of the following parameters is required for this route:

Parameter Type Description
isPrivate Boolean This flag indicates that the metadata for the Codex Record is private and can only be retrieved by the owner, the approvedAddress, and the addresses listed in whitelistedAddresses / whitelistedEmails.
whitelistedEmails Array[String] An array of email addresses allowed to view private metadata for this Codex Record. This allows users to give people read-only access to their Codex Records.
whitelistedAddresses Array[String] An array of Ethereum addresses allowed to view private metadata for this Codex Record. This allows users to give people read-only access to their Codex Records.
isHistoricalProvenancePrivate Boolean This flag indicates whether or not historical provenance (i.e. metadata.files) should be hidden, regardless of the value of isPrivate.

Modify a Codex Record's Metadata

This endpoint can be used to modify the "on-chain" data for a Codex Record. With this request, you're actually creating a Pending Update since the metadata itself can't be modified until the transaction has been mined on the blockchain.

import fs from 'fs'
import request from 'request'

const options = {
  url: 'https://rinkeby-api.codexprotocol.com/v1/client/record/0/metadata',
  method: 'put',
  json: true,

  headers: {
    Authorization: 'Bearer d49694e5a3459759cc7ac1741de246e184e51d6e',
  },

  // by using "formData" here instead of "body", request will send this request
  //  as multipart/form-data, which is necessary for sending file data
  formData: {
    name: 'Super Duper New Title',
    description: 'This will replace the current description!',

    // replace the current mainImage
    newMainImage: fs.createReadStream('/tmp/uploads/super-duper-new-main-image.jpg'),

    // add these files to the files & images arrays
    newFiles: [
      fs.createReadStream('/tmp/uploads/2018-appraisal-documents.pdf'),
    ],
    newImages: [
      fs.createReadStream('/tmp/uploads/super-duper-new-photo-1.jpg'),
      fs.createReadStream('/tmp/uploads/super-duper-new-photo-2.jpg'),
    ],

    // before adding new files & images, replace the existing arrays with these
    // see "Removing Files and Images" for details
    files: [
      { id: '5bb640d492d258fdf2efa290', },
      { id: '5bb640d492d258fdf2efa291', },
    ],
    images: [
      { id: '5bb640d492d258fdf2efa292', },
      { id: '5bb640d492d258fdf2efa293', },
    ],
  },
}

request(options, (error, response) => {
  console.log(response.body.result) // a new Pending Update
})

The above API call returns a new Pending Update.

HTTP Request

PUT /v1/client/record/:tokenId/metadata

Webhook Event

Event Name Recipient
codex-record:modified Owner

URL Parameters

Parameter Type Description
tokenId Number The tokenId of the Codex Record for which to modify the metadata of.

Request Parameters

At least one of the following parameters is required for this route:

Parameter Type Description
name String The new name of this Codex Record.
files Array[File] See Removing Files and Images for details.
images Array[File] See Removing Files and Images for details.
newFiles Array[File Data] An array of supplemental files to append to the existing files array for this Codex Record.
newImages Array[File Data] An array of supplemental images to append to the existing images array for this Codex Record.
description String The new description of this Codex Record. The field can be set to null to erase an existing description.
newMainImage File Data The new main image of this Codex Record.









Removing Files and Images

Removing the File with id 5bb640d492d258fdf2efa294 from a Codex Record's images array:

import request from 'request'

// first, get the existing images
const getCodexRecordOptions = {
  url: 'https://rinkeby-api.codexprotocol.com/v1/records/0/images',
  method: 'get',
  json: true,

  headers: {
    Authorization: 'Bearer d49694e5a3459759cc7ac1741de246e184e51d6e',
  },
}

request(getCodexRecordOptions, (getCodexRecordError, getCodexRecordResponse) => {

  const codexRecordImages = getCodexRecordResponse.body.result

  // the remove the image with id '5bb640d492d258fdf2efa294'
  const newCodexRecordImages = codexRecordImages
    .filter((image) => {
      return image.id !== '5bb640d492d258fdf2efa294'
    })

    // this map is optional since the the only field you're required to send in
    //  is the File's id
    //
    // it's perfectly fine to send the entire File object, but it's a good idea
    //  to reduce the amount of unnecessary data sent with requests
    .map((image) => {
      return { id: image.id }
    })

  // now send in the "new state" of the images array
  const updateCodexRecordOptions = {
    url: 'https://rinkeby-api.codexprotocol.com/v1/client/record/0/metadata',
    method: 'put',
    json: true,

    headers: {
      Authorization: 'Bearer d49694e5a3459759cc7ac1741de246e184e51d6e',
    },

    body: {
      images: newCodexRecordImages,
    },
  }

  request(updateCodexRecordOptions, (updateCodexRecordError, updateCodexRecordResponse) => {
    console.log(updateCodexRecordResponse.body.result) // a new Pending Update
  })

})

The files and images parameters are each arrays of File objects (not to be confused with binary "File Data" in a multipart/form-data request...) These parameters provide the mechanism for removing files and/or images.

To remove files and/or images from a Codex Record, follow these steps:

  1. If you do not already have the Codex Record, you will need to retrieve the existing files and/or images first. You can get this data by getting the whole Codex Record, getting the Codex Record's files only, and/or getting the Codex Record's images only.

  2. Remove the images from the array(s).

  3. Send the "new state" of the array(s) with this request as files and/or images.

Start a Transfer

This endpoint can be used to start the process of transferring a Codex Record to another person. Transferring is a two step process, see Transferring Codex Records for details.

import request from 'request'

const options = {
  url: 'https://rinkeby-api.codexprotocol.com/v1/client/records/0/transfer/approve',
  method: 'put',
  json: true,

  headers: {
    Authorization: 'Bearer d49694e5a3459759cc7ac1741de246e184e51d6e',
  },

  // address and email are mutually exclusive, choose one or the other but not both
  body: {
    // email: 'email@example.com',
    address: '0xf17f52151ebef6c7334fad080c5704d77216b732',
  },
}

request(options, (error, response) => {
  console.log(response.body.result)
})

The above API call returns the Codex Record being transferred, although no data will have changed at this point.

HTTP Request

PUT /v1/client/records/:tokenId/transfer/approve

Webhook Event

Event Name Recipient
codex-record:address-approved:owner Owner
codex-record:address-approved:approved The newly approved user

URL Parameters

Parameter Type Description
tokenId Number The tokenId of the Codex Record to transfer.

Request Parameters

Parameter Type Description
email String (Required if address is not specified) The email address of the person to transfer this record to.
address String (Required if email is not specified) The Ethereum address of the person to transfer this record to.

Cancel a Transfer

This endpoint can be used to stop the process of transferring a Codex Record to another person. This essentially removes the currently set approvedAddress, preventing that address from accepting the transfer (and removing it from their incoming transfers list.) See Transferring Codex Records for details.

import request from 'request'

const options = {
  url: 'https://rinkeby-api.codexprotocol.com/v1/client/records/0/transfer/cancel',
  method: 'put',
  json: true,

  headers: {
    Authorization: 'Bearer d49694e5a3459759cc7ac1741de246e184e51d6e',
  },
}

request(options, (error, response) => {
  console.log(response.body.result)
})

The above API call returns the Codex Record for which the transfer is being cancelled, although no data will have changed at this point.

HTTP Request

PUT /v1/client/records/:tokenId/transfer/cancel

URL Parameters

Parameter Type Description
tokenId Number The tokenId of the Codex Record for which to cancel the transfer of.

Accept a Transfer

This endpoint can be used to accept an incoming transfer of a Codex Record. See Transferring Codex Records for details.

import request from 'request'

const options = {
  url: 'https://rinkeby-api.codexprotocol.com/v1/client/records/0/transfer/accept',
  method: 'put',
  json: true,

  headers: {
    Authorization: 'Bearer d49694e5a3459759cc7ac1741de246e184e51d6e',
  },
}

request(options, (error, response) => {
  console.log(response.body.result)
})

The above API call returns the Codex Record for which the transfer is being accepted, although no data will have changed at this point.

HTTP Request

PUT /v1/client/records/:tokenId/transfer/accept

Webhook Event

Event Name Recipient
codex-record:transferred:old-owner The old owner
codex-record:transferred:new-owner The new owner

URL Parameters

Parameter Type Description
tokenId Number The tokenId of the Codex Record for which to accept the transfer of.

Ignore a Transfer

This endpoint can be used to mark an incoming transfer as "ignored". This is useful to (visually) remove a transfer from your incoming transfers list without accepting it, because there's no blockchain mechanism to explicitly reject a transfer. See Transferring Codex Records for details.

This route sets the Codex Record's isIgnored property to true. See Codex Record for details.

import request from 'request'

const options = {
  url: 'https://rinkeby-api.codexprotocol.com/v1/client/records/0/transfer/ignore',
  method: 'put',
  json: true,

  headers: {
    Authorization: 'Bearer d49694e5a3459759cc7ac1741de246e184e51d6e',
  },
}

request(options, (error, response) => {
  console.log(response.body.result) // the immediately-updated Codex Record
})

The above API call returns the immediately-updated Codex Record, with isIgnored set to true.

HTTP Request

PUT /v1/client/records/:tokenId/transfer/ignore

URL Parameters

Parameter Type Description
tokenId Number The tokenId of the Codex Record for which to ignore the transfer of.

Get Outgoing Transfers

This endpoint can be used to retrieve a list of your application's outgoing transfers. Outgoing transfers are Codex Records that your application is owner of, which also have an approvedAddress set. See Transferring Codex Records for details.

import request from 'request'

const options = {
  url: 'https://rinkeby-api.codexprotocol.com/v1/client/transfers/outgoing',
  method: 'get',
  json: true,

  headers: {
    Authorization: 'Bearer d49694e5a3459759cc7ac1741de246e184e51d6e',
  },

  // sort by date created in reverse and get 5 records, skipping the first ten
  body: {
    limit: 5,
    offset: 10,
    order: '-createdAt',
  },
}

request(options, (error, response) => {
  console.log(response.body.result) // your application's outgoing transfers
})

The above API call returns an array of Codex Records.

HTTP Request

GET /v1/client/transfers/outgoing

Request Parameters

Parameter Type Default Description
limit Number 100 How many records to retrieve. Use with offset to paginate through outgoing transfers.
offset Number 0 How many records to skip before applying the limit. Use with limit to paginate through outgoing transfers.
order String createdAt To sort in reverse order, specify this value as -createdAt.

Get Incoming Transfers

This endpoint can be used to retrieve a list of your application's incoming transfers. Incoming transfers are Codex Records that your application does not own, which also have their approvedAddress property set to your application's Ethereum address. See Transferring Codex Records for details.

import request from 'request'

const options = {
  url: 'https://rinkeby-api.codexprotocol.com/v1/client/transfers/incoming',
  method: 'get',
  json: true,

  headers: {
    Authorization: 'Bearer d49694e5a3459759cc7ac1741de246e184e51d6e',
  },

  // sort by date created in reverse and get 5 records, skipping the first ten
  body: {
    limit: 5,
    offset: 10,
    order: '-createdAt',

    // don't return ignored transfers
    filters: {
      isIgnored: false,
    },
  },
}

request(options, (error, response) => {
  console.log(response.body.result) // your application's incoming transfers
})

The above API call returns an array of Codex Records.

HTTP Request

GET /v1/client/transfers/incoming

Request Parameters

Parameter Type Default Description
limit Number 100 How many records to retrieve. Use with offset to paginate through incoming transfers.
offset Number 0 How many records to skip before applying the limit. Use with limit to paginate through incoming transfers.
order String createdAt To sort in reverse order, specify this value as -createdAt.
filters[isIgnored] Boolean This can be used to return only ignored transfers (when true), or only non-ignored transfers (when false).

Get a Codex Record's Whitelisted Addresses

This endpoint can be used to retrieve a Codex Record's whitelistedAddresses property, an array of Ethereum addresses allowed to view private metadata for this Codex Record. See Whitelisted Addresses for details.

import request from 'request'

const options = {
  url: 'https://rinkeby-api.codexprotocol.com/v1/client/records/0/whitelisted-addresses',
  method: 'get',
  json: true,

  headers: {
    Authorization: 'Bearer d49694e5a3459759cc7ac1741de246e184e51d6e',
  },
}

request(options, (error, response) => {
  console.log(response.body.result) // the whitelistedAddresses array for this Codex Record
})

The above API call returns an array of Ethereum addresses (strings).

HTTP Request

GET /v1/client/records/:tokenId/whitelisted-addresses

URL Parameters

Parameter Type Description
tokenId Number The tokenId of the Codex Record for which to retrieve the whitelisted addresses of.

Add an Address to a Codex Record's Whitelisted Addresses

This endpoint can be used to add a single Ethereum address to a Codex Record's whitelistedAddresses array. The address is then allowed to view private metadata for this Codex Record, effectively giving them read-only access. See Whitelisted Addresses for details.

import request from 'request'

const options = {
  url: 'https://rinkeby-api.codexprotocol.com/v1/client/records/0/whitelisted-addresses',
  method: 'post',
  json: true,

  headers: {
    Authorization: 'Bearer d49694e5a3459759cc7ac1741de246e184e51d6e',
  },

  body: {
    address: '0xf17f52151ebef6c7334fad080c5704d77216b732',
  },
}

request(options, (error, response) => {
  console.log(response.body.result) // the updated whitelistedAddresses array for this Codex Record
})

The above API call returns the updated whitelistedAddresses array.

HTTP Request

POST /v1/client/records/:tokenId/whitelisted-addresses

Webhook Event

Event Name Recipient
codex-record:address-whitelisted The newly whitelisted user

URL Parameters

Parameter Type Description
tokenId Number The tokenId of the Codex Record for which to retrieve the whitelisted addresses of.

Request Parameters

Parameter Type Description
address String The Ethereum address to add to the list of whitelisted addresses.

Remove an Address from a Codex Record's Whitelisted Addresses

This endpoint can be used to remove a single Ethereum address from a Codex Record's whitelistedAddresses array, revoking their ability to view the private metadata. See Whitelisted Addresses for details.

import request from 'request'

const options = {
  url: 'https://rinkeby-api.codexprotocol.com/v1/client/records/0/whitelisted-addresses',
  method: 'delete',
  json: true,

  headers: {
    Authorization: 'Bearer d49694e5a3459759cc7ac1741de246e184e51d6e',
  },

  body: {
    address: '0xf17f52151ebef6c7334fad080c5704d77216b732',
  },
}

request(options, (error, response) => {
  // no response bodies for DELETE requests
})

The above API call returns no response body, but a 204 status code will be returned to indicate a successful deletion.

HTTP Request

DELETE /v1/client/records/:tokenId/whitelisted-addresses

URL Parameters

Parameter Type Description
tokenId Number The tokenId of the Codex Record for which to retrieve the whitelisted addresses of.

Request Parameters

Parameter Type Description
address String The Ethereum address to remove from the list of whitelisted addresses.

Entirely Replace a Codex Record's Whitelisted Addresses

This endpoint can be used to entirely replace a Codex Record's whitelistedAddresses array, instead of adding and removing one-by-one. See Whitelisted Addresses for details.

import request from 'request'

const options = {
  url: 'https://rinkeby-api.codexprotocol.com/v1/client/records/0/whitelisted-addresses',
  method: 'put',
  json: true,

  headers: {
    Authorization: 'Bearer d49694e5a3459759cc7ac1741de246e184e51d6e',
  },

  body: {
    addresses: [
      '0x821aea9a577a9b44299b9c15c88cf3087f3b5544',
      '0x0d1d4e623d10f9fba5db95830f7d3839406c6af2',
      '0x2932b7a2355d6fecc4b5c0b6bd44cc31df247a2e',
      '0x2191ef87e392377ec08e7c08eb105ef5448eced5',
      '0x0f4f2ac550a1b4e2280d04c21cea7ebd822934b5',
    ],
  },
}

request(options, (error, response) => {
  console.log(response.body.result) // the updated whitelistedAddresses array for this Codex Record
})

The above API call returns an array of Ethereum addresses (strings).

HTTP Request

PUT /v1/client/records/:tokenId/whitelisted-addresses

Webhook Event

Event Name Recipient
codex-record:address-whitelisted Each newly whitelisted user

URL Parameters

Parameter Type Description
tokenId Number The tokenId of the Codex Record for which to retrieve the whitelisted addresses of.

Request Parameters

Parameter Type Description
addresses Array[String] An array of Ethereum addresses to replace the current whitelisted addresses with.

Get a Codex Record's Whitelisted Emails

This endpoint can be used to retrieve a Codex Record's whitelistedEmails property, an array of email addresses allowed to view private metadata for this Codex Record. See Whitelisted Addresses for details.

import request from 'request'

const options = {
  url: 'https://rinkeby-api.codexprotocol.com/v1/client/records/0/whitelisted-emails',
  method: 'get',
  json: true,

  headers: {
    Authorization: 'Bearer d49694e5a3459759cc7ac1741de246e184e51d6e',
  },
}

request(options, (error, response) => {
  console.log(response.body.result) // the whitelistedEmails array for this Codex Record
})

The above API call returns an array of email addresses (strings).

HTTP Request

GET /v1/client/records/:tokenId/whitelisted-emails

URL Parameters

Parameter Type Description
tokenId Number The tokenId of the Codex Record for which to retrieve the whitelisted emails of.

Add an Email to a Codex Record's Whitelisted Emails

This endpoint can be used to add a single email address to a Codex Record's whitelistedEmails array. The address is then allowed to view private metadata for this Codex Record, effectively giving them read-only access. See Whitelisted Addresses for details.

import request from 'request'

const options = {
  url: 'https://rinkeby-api.codexprotocol.com/v1/client/records/0/whitelisted-emails',
  method: 'post',
  json: true,

  headers: {
    Authorization: 'Bearer d49694e5a3459759cc7ac1741de246e184e51d6e',
  },

  body: {
    email: '0xf17f52151ebef6c7334fad080c5704d77216b732',
  },
}

request(options, (error, response) => {
  console.log(response.body.result) // the updated whitelistedEmails array for this Codex Record
})

The above API call returns the updated whitelistedEmails array.

HTTP Request

POST /v1/client/records/:tokenId/whitelisted-emails

Webhook Event

Event Name Recipient
codex-record:address-whitelisted The newly whitelisted user

URL Parameters

Parameter Type Description
tokenId Number The tokenId of the Codex Record for which to retrieve the whitelisted emails of.

Request Parameters

Parameter Type Description
email String The email address to add to the list of whitelisted emails.

Remove an Email from a Codex Record's Whitelisted Emails

This endpoint can be used to remove a single email address from a Codex Record's whitelistedEmails array, revoking their ability to view the private metadata. See Whitelisted Addresses for details.

import request from 'request'

const options = {
  url: 'https://rinkeby-api.codexprotocol.com/v1/client/records/0/whitelisted-emails',
  method: 'delete',
  json: true,

  headers: {
    Authorization: 'Bearer d49694e5a3459759cc7ac1741de246e184e51d6e',
  },

  body: {
    email: '0xf17f52151ebef6c7334fad080c5704d77216b732',
  },
}

request(options, (error, response) => {
  // no response bodies for DELETE requests
})

The above API call returns no response body, but a 204 status code will be returned to indicate a successful deletion.

HTTP Request

DELETE /v1/client/records/:tokenId/whitelisted-emails

URL Parameters

Parameter Type Description
tokenId Number The tokenId of the Codex Record for which to retrieve the whitelisted emails of.

Request Parameters

Parameter Type Description
email String The email address to remove from the list of whitelisted emails.

Entirely Replace a Codex Record's Whitelisted Emails

This endpoint can be used to entirely replace a Codex Record's whitelistedEmails array, instead of adding and removing one-by-one. See Whitelisted Addresses for details.

import request from 'request'

const options = {
  url: 'https://rinkeby-api.codexprotocol.com/v1/client/records/0/whitelisted-emails',
  method: 'put',
  json: true,

  headers: {
    Authorization: 'Bearer d49694e5a3459759cc7ac1741de246e184e51d6e',
  },

  body: {
    addresses: [
      '0x821aea9a577a9b44299b9c15c88cf3087f3b5544',
      '0x0d1d4e623d10f9fba5db95830f7d3839406c6af2',
      '0x2932b7a2355d6fecc4b5c0b6bd44cc31df247a2e',
      '0x2191ef87e392377ec08e7c08eb105ef5448eced5',
      '0x0f4f2ac550a1b4e2280d04c21cea7ebd822934b5',
    ],
  },
}

request(options, (error, response) => {
  console.log(response.body.result) // the updated whitelistedEmails array for this Codex Record
})

The above API call returns an array of email addresses (strings).

HTTP Request

PUT /v1/client/records/:tokenId/whitelisted-emails

Webhook Event

Event Name Recipient
codex-record:address-whitelisted Each newly whitelisted user

URL Parameters

Parameter Type Description
tokenId Number The tokenId of the Codex Record for which to retrieve the whitelisted emails of.

Request Parameters

Parameter Type Description
addresses Array[String] An array of email addresses to replace the current whitelisted emails with.

Request Faucet Drip

This endpoint can be used to request CODX from the faucet in Testnets. See CODX Tokens & Fees for details.

import request from 'request'

const options = {
  url: 'https://rinkeby-api.codexprotocol.com/v1/client/faucet/drip',
  method: 'get',
  json: true,

  headers: {
    Authorization: 'Bearer d49694e5a3459759cc7ac1741de246e184e51d6e',
  },
}

request(options, (error, response) => {
  // nothing returned by this route
})

The above API call returns nothing, but a 200 response code means the request was successful

HTTP Request

PUT /v1/client/faucet/drip

Webhook Event

Event Name Recipient
codex-coin:transferred Drip requester (your application)

Bulk Routes

Bulk routes are a subset of client routes, which means they are also prefixed with /v1/client and only perform actions on data owned by your application. All bulk routes require an accessToken in the Authorization header, as described in the Access Tokens section.

However, bulk routes are listed separately here because they work a bit differently. They are designed to take large sets of data and process actions in a queue. For example, these bulk routes can be used to create hundreds of Codex Records with just one API request.

Create Codex Records in Bulk

This endpoint can be used to create a large number of Codex Records with just one API request. This route is essentially the same as the single-record creation route, but it takes an array of metadata objects instead of a single metadata object. Another difference is that this route takes URLs for files and NOT file data (these files are downloaded an re-hosted by Codex during processing.)

import request from 'request'

const options = {
  url: 'https://rinkeby-api.codexprotocol.com/v1/client/bulk-transaction/record-mint',
  method: 'post',
  json: true,

  headers: {
    Authorization: 'Bearer d49694e5a3459759cc7ac1741de246e184e51d6e',
  },

  body: {
    generateClaimCode: true,
    proxyUserAddress: '0xf17f52151ebef6c7334fad080c5704d77216b732'
    metadata: [
      {
        name: 'Really Cool Codex Record #1',
        description: 'This is a really cool Codex Record!',

        mainImageUrl: 'https://example.com/images/cool-main-image.jpg',
        imageUrls: [
          'https://example.com/images/cool-supplemental-image-1.jpg',
          'https://example.com/images/cool-supplemental-image-2.jpg',
        ],
        fileUrls: [
          'https://example.com/files/author-notes.txt',
          'https://example.com/files/certificate-of-authenticity.pdf',
        ],

        isPrivate: true,
        isHistoricalProvenancePrivate: true,
        whitelistedEmails: [
          'user-1@example.com',
        ],
        whitelistedAddresses: [
          '0xf17f52151ebef6c7334fad080c5704d77216b732',
          '0xc5fdf4076b8f3a5357c5e395ab970b5b54098fef',
        ],

        additionalMetadata: {
          lotNumber: '123A',
          salePrice: '1000',
          saleCurrency: 'USD',
          editionNumber: '16'
          totalEditions: '64'
          condition: 'Excellent',
          dimensionsInches: '18 x 23',
          creatorName: 'Cool Guy McGee',
          auctionName: 'My Cool Auction',
          medium: 'mixed media on paper',
          signatureLocation: 'front, top left',
          dimensionsCentimeters: '45.7 x 58.4',
          assetSoldAt: '2019-03-21T16:30:06.030Z',
          assetCreatedAt: '1993-01-01T00:00:00.000Z',
        },

        auctionHouseMetadata: {
          id: '1234',
          linkbackUrl: 'https://example.com/my-auction-house/asset/1234',

          // any other arbitrary fields
          transactionId: 27299,
          inventoryNumber: 'TWC1123-G16926-001',
        },
      },
      {
        name: 'Really Cool Codex Record #2',
        description: 'This is also a really cool Codex Record!',

        mainImageUrl: 'https://example.com/images/another-cool-main-image.jpg',
        imageUrls: [
          'https://example.com/images/another-cool-supplemental-image-1.jpg',
          'https://example.com/images/another-cool-supplemental-image-2.jpg',
        ],
        fileUrls: [
          'https://example.com/files/more-author-notes.txt',
          'https://example.com/files/another-certificate-of-authenticity.pdf',
        ],

        isPrivate: false,
        isHistoricalProvenancePrivate: false,
        whitelistedEmails: [
          'user-2@example.com',
        ],
        whitelistedAddresses: [
          '0x821aea9a577a9b44299b9c15c88cf3087f3b5544',
          '0x0d1d4e623d10f9fba5db95830f7d3839406c6af2',
        ],

        additionalMetadata: {
          lotNumber: '123A',
          condition: 'Poor',
          editionNumber: '16'
          totalEditions: '64'
          salePrice: '2000',
          saleCurrency: 'USD',
          medium: 'linocut in 4 colors',
          creatorName: 'Cool Guy McGee',
          auctionName: 'My Cool Auction',
          dimensionsInches: '7 1/2 x 11 3/4',
          dimensionsCentimeters: '19.1 x 29.8',
          signatureLocation: 'back, bottom left',
          assetSoldAt: '2019-03-20T14:30:06.030Z',
          assetCreatedAt: '1976-01-01T00:00:00.000Z',
        },

        auctionHouseMetadata: {
          id: '4321',
          linkbackUrl: 'https://example.com/my-auction-house/asset/4321',

          // any other arbitrary fields
          transactionId: 27300,
          inventoryNumber: 'TWC1123-G16926-002',
        },
      },

      // more metadata objects here

    ],
  },
}

request(options, (error, response) => {
  console.log(response.body.result) // a Bulk Transaction object
})

The above API call (eventually) creates two Codex Records and returns a single Bulk Transaction.

HTTP Request

POST /v1/client/bulk-transaction/record-mint

Webhook Events

Event Name Recipient
bulk-transaction:started Bulk Transaction creator (and not the proxy user, if specified)
bulk-transaction:completed Bulk Transaction creator (and not the proxy user, if specified)

Request Parameters

Parameter Type Description
proxyUserAddress String (Optional) The Ethereum address of the user to create these Codex Records for. This field is only for applications with Proxy Users.
generateClaimCode Boolean (Optional, default false) Generate a claimCode (and claimCodeUrl) that can subsequently be used users of your application to claim the resulting Bulk Transaction, automatically transferring the Codex Records to their account.
metadata[][name] String The plain text name of this Codex Record.
metadata[][description] String (Optional) The plain text description of this Codex Record. The field can be set to null or simply omitted to leave the description empty.
metadata[][mainImageUrl] String The main image of this Codex Record.
metadata[][imageUrls] String (Optional) An array of supplemental images that belong to this metadata.
metadata[][fileUrls] String (Optional) An array of supplemental files that belong to this metadata. This is considered the "historical provenance" of the Codex Record.
metadata[][additionalMetadata] Object (Optional) A list of additional metadata about this asset. See Additional Metadata for details.
metadata[][auctionHouseMetadata] Object An arbitrary list of data specific to your application (this field is ignored if your application is not an auction house.) IF your account is an auction house account, this field is required (as well as the auctionHouseMetadata.id parameter.) See Auction House Metadata for details.
metadata[][isPrivate] Boolean (Optional, default false) This flag indicates that the metadata for the Codex Record is private and can only be retrieved by the owner, the approvedAddress, and the addresses listed in whitelistedAddresses / whitelistedEmails.
metadata[][isHistoricalProvenancePrivate] Boolean (Optional, default true) This flag indicates whether or not historical provenance (i.e. metadata[][files]) should be hidden, regardless of the value of isPrivate.
metadata[][whitelistedEmails] Array[String] (Optional) An array of email addresses allowed to view private metadata for this Codex Record. This allows users to give people read-only access to their Codex Records.
metadata[][whitelistedAddresses] Array[String] (Optional) An array of Ethereum addresses allowed to view private metadata for this Codex Record. This allows users to give people read-only access to their Codex Records.

Convert Metadata XML into JSON

It may be easier for your application to export metadata as XML instead of JSON, so a utility route exists to simply convert an XML file into a JSON response. You can then use this JSON response as the metadata array when creating Codex Records in bulk.

import fs from 'fs'
import request from 'request'

const convertXMLRequestOptions = {
  url: 'https://rinkeby-api.codexprotocol.com/v1/client/bulk-transactions/record-mint/convert/xml',
  method: 'post',
  json: true,

  headers: {
    Authorization: 'Bearer d49694e5a3459759cc7ac1741de246e184e51d6e',
  },

  // by using "formData" here instead of "body", request will send this request
  //  as multipart/form-data, which is necessary for sending file data
  formData: {
    mainImage: fs.createReadStream('/tmp/xml-metadata.xml'),
  },
}

request(convertXMLRequestOptions, (convertXMLError, convertXMLResponse) => {

  // now take the JSON metadata and send it to the bulk transaction route...
  const metadata = convertXMLResponse.body.result

  const createBulkTransactionRequestOptions = {
    url: 'https://rinkeby-api.codexprotocol.com/v1/client/bulk-transaction/record-mint',
    method: 'post',
    json: true,

    headers: {
      Authorization: 'Bearer d49694e5a3459759cc7ac1741de246e184e51d6e',
    },

    body: {
      metadata,
    },
  }

  request(createBulkTransactionRequestOptions, (createBulkTransactionError, createBulkTransaction) => {
    console.log(createBulkTransaction.body.result) // a Bulk Transaction object
  })

})

The above API sends an XML file, retrieves converted JSON in the response, and sends that JSON metadata to the create Codex Records in Bulk route.

HTTP Request

POST /v1/client/bulk-transactions/record-mint/convert/xml

Request Parameters

Parameter Type Description
metadata File Data The XML file to convert.































Example XML Structure

<Records>
  <Record>
    <!-- required -->
    <Name>Really Cool Codex Record #1</Name>
    <Description>This is a really cool Codex Record!</Description>
    <MainImageUrl>https://example.com/images/cool-main-image.jpg</MainImageUrl>

    <!-- optional additional images -->
    <ImageUrl>https://example.com/images/cool-supplemental-image-1.jpg</ImageUrl>
    <ImageUrl>https://example.com/images/cool-supplemental-image-2.jpg</ImageUrl>

    <!-- optional additional files -->
    <FileUrl>https://example.com/files/author-notes.txt</FileUrl>
    <FileUrl>https://example.com/files/certificate-of-authenticity.pdf</FileUrl>

    <IsPrivate>false</IsPrivate>
    <IsHistoricalProvenancePrivate>false</IsHistoricalProvenancePrivate>

    <!-- optional -->
    <AdditionalMetadata>
      <LotNumber>123A</LotNumber>
      <SalePrice>1000</SalePrice>
      <SaleCurrency>USD</SaleCurrency>
      <Condition>Excellent</Condition>
      <EditionNumber>16</EditionNumber>
      <TotalEditions>64</TotalEditions>
      <Medium>mixed media on paper</Medium>
      <CreatorName>Cool Guy McGee</CreatorName>
      <AuctionName>My Cool Auction</AuctionName>
      <DimensionsInches>18 x 23</DimensionsInches>
      <AssetSoldAt>2019-03-21T16:30:06.030Z</AssetSoldAt>
      <SignatureLocation>front, top left</SignatureLocation>
      <AssetCreatedAt>1993-01-01T00:00:00.000Z</AssetCreatedAt>
      <DimensionsCentimeters>45.7 x 58.4</DimensionsCentimeters>
    </AdditionalMetadata>

    <AuctionHouseMetadata>
      <!-- required -->
      <Id>1234</Id>

      <!-- optional -->
      <LinkbackUrl>https://www.heffel.com/auction/Details_E.aspx?ID=30510</LinkbackUrl>

      <!-- any other arbitrary fields -->
      <TransactionID>27299</TransactionID>
      <InventoryNumber>TWC1123-G16926-001</InventoryNumber>
    </AuctionHouseMetadata>
  </Record>

  <!-- more <Record> elements here -->

</Records>

The structure of XML metadata is very similar to the structure of JSON metadata. Please see the example in this section to guide you when constructing your XML metadata.

Get a Bulk Transaction

This endpoint can be used to retrieve a specific Bulk Transaction that your application has created. This data can be used to monitor the progress of an in-progress Bulk Transaction, or to retrieve stats about an already-completed Bulk Transaction (such as how many Codex Records were created.)

import request from 'request'

const options = {
  url: 'https://rinkeby-api.codexprotocol.com/v1/client/bulk-transactions/5c8fe94329de5e47fb9df266',
  method: 'get',
  json: true,
}

request(options, (error, response) => {
  console.log(response.body.result) // Bulk Transaction with id 5c8fe94329de5e47fb9df266
})

The above API call returns a single Bulk Transaction.

HTTP Request

GET /v1/client/bulk-transactions/:bulkTransactionId

URL Parameters

Parameter Type Description
bulkTransactionId String The id of the Bulk Transaction to retrieve.

Get All Bulk Transactions

This endpoint can be used to retrieve all Bulk Transactions that your application has created. This data can be used to monitor the progress of in-progress Bulk Transactions, or to retrieve stats about a already-completed Bulk Transactions (such as how many Codex Records were created.)

import request from 'request'

const options = {
  url: 'https://rinkeby-api.codexprotocol.com/v1/client/bulk-transactions',
  method: 'get',
  json: true,

  headers: {
    Authorization: 'Bearer d49694e5a3459759cc7ac1741de246e184e51d6e',
  },

  // sort by date created in reverse and get 5 records, skipping the first ten
  body: {
    limit: 5,
    offset: 10,
    order: '-createdAt',
  },
}

request(options, (error, response) => {
  console.log(response.body.result) // an array of your application's Bulk Transactions
})

The above API call returns an array of Bulk Transactions.

HTTP Request

GET /v1/client/bulk-transactions

Request Parameters

Parameter Type Default Description
limit Number 100 How many records to retrieve. Use with offset to paginate through Bulk Transactions.
offset Number 0 How many records to skip before applying the limit. Use with limit to paginate through Bulk Transactions.
order String -createdAt To sort in chronological order, specify this value as createdAt.

Claim a Bulk Transaction

If your application creates Codex Records with the intention of transferring them to your users, it is possible to automate this by creating Codex Records in bulk with the generateClaimCode boolean set to true. This will cause the resulting Bulk Transaction to have a claimCode (and claimCodeUrl) that can be passed on to one of your users.

Visiting the claimCodeUrl will present the user with a summary of the Bulk Transaction and provide a way for them to create a new Codex Viewer account (or log into an existing account) to "claim" all Codex Records. After claiming the Bulk Transaction, the Codex Records will be automatically transferred to the claiming user's account.

Here is an example of the Claim Records page (i.e. claimCodeUrl):

Claim records screenshot

import request from 'request'

const options = {
  url: 'https://rinkeby-api.codexprotocol.com/v1/client/claim-records/4ACsjkrR',
  method: 'post',
  json: true,

  headers: {
    Authorization: 'Bearer d49694e5a3459759cc7ac1741de246e184e51d6e',
  },
}

request(options, (error, response) => {
  console.log(response.body.result) // the claimed Bulk Transaction object
})

The above API call (eventually) transfers all Codex Records belonging to the Bulk Transaction with claimCode "4ACsjkrR" and returns the claimed Bulk Transaction.

You can also claim a Bulk Transaction programmatically by calling the following route. In this case, your application would be claiming another Bulk Transaction (not created by your application.)

HTTP Request

POST v1/client/claim-records/:claimCode

Webhook Events

Event Name Recipient
bulk-transaction:claim:completed:creator Bulk Transaction creator
bulk-transaction:claim:completed:claim-user The user who claimed the Bulk Transaction

General Concepts

This section explains miscellaneous concepts referenced throughout this documentation.

API Requests

This HTTP request:

PUT /v1/client/record/100/metadata?name=Really%20Cool%20Codex%20Record HTTP/1.1
Content-Type: application/x-www-form-urlencoded
Host: https://rinkeby-api.codexprotocol.com
Authorization: Bearer d49694e5a3459759cc7ac1741de246e184e51d6e

description=This+is+a+really+cool+Codex+Record!

is handled identically to this one:

PUT /v1/client/record/0/metadata HTTP/1.1
Content-Type: application/json
Host: https://rinkeby-api.codexprotocol.com
Authorization: Bearer e933e3e02e4f16d552e469367b25293d75c3d3c2

{
  "name": "Really Cool Codex Record",
  "description": "This is a really cool Codex Record!"
}

and this one:

PUT /v1/client/record/0/metadata?description=This%20is%20a%20really%20cool%20Codex%20Record! HTTP/1.1
Host: https://rinkeby-api.codexprotocol.com
Authorization: Bearer e933e3e02e4f16d552e469367b25293d75c3d3c2
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW

------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="name"

Really Cool Codex Record
------WebKitFormBoundary7MA4YWxkTrZu0gW--

The Codex API is very flexible with how it handles incoming requests. For most routes, the API will accept a request with a Content-Type of multipart/form-data, application/x-www-form-urlencoded, or application/json. Additionally, you may specify request parameters as either query parameters or body parameters (with the exception of file data, of course.)

The exceptions are:

  1. Anytime you send file data with the request, you must send the request as Content-Type: multipart/form-data.

  2. When requesting access tokens, you must send the request as Content-Type: application/x-www-form-urlencoded. This is required by the OAuth2 Specification.

For details on what to expect from The Codex API in response, see Response Examples.

Webhooks

Calling certain routes may cause asynchronous transactions to be submitted to the blockchain. Specifically, any action that modifies "on-chain" data is asynchronous and has an associated webhook event.

Your application should not assume the result of calling an asynchronous route will be performed immediately. Instead, your application should have an endpoint that can be called by The Codex API when the asynchronous action is complete and the blockchain state has been updated.

For example, when updating modifying a Codex Record's Metadata, your application should wait until it's webhook is called with the codex-record:modified event. At this point your application can react to the updated data in any way necessary.

Asynchronous Actions

The following actions should be considered asynchronous, and your application should wait for their associated webhook events before considering requests sent to them as successful.

Action Event(s)
Creating a Codex Record codex-record:created
Modifying a Codex Record's Metadata codex-record:modified
Starting a Transfer codex-record:address-approved:owner and codex-record:address-approved:approved
Canceling a Transfer codex-record:address-approved:cancel
Accepting a Transfer codex-record:transferred:old-owner and codex-record:transferred:new-owner
Creating Codex Records in Bulk bulk-transaction:started and bulk-transaction:completed
Claiming a Bulk Transaction bulk-transaction:claim:completed:creator and bulk-transaction:claim:completed:claim-user

Webhook Structure

An example request body sent to your application's webhook:

{
  "timestamp": 1539024178127,
  "hash": "5fed9779c702a873e8c68872eca959876d9d00a84a1cb5fb58ecd6c3e83f307b",
  "network": {
    "id": "4",
    "name": "rinkeby"
  },
  "eventName": "codex-record:created",
  "eventData": {
    // varies by event, usually a Codex Record
  }
}

When blockchain events occur, your application's webhook will be sent a POST request with an application/json body containing the following data:

Property Type Description
hash String The sha256 hash of the string ${timestamp}:${client_id}, using your application's secret as the key. See Verifying Webhook Requests for details.
timestamp Number Unix timestamp (in milliseconds) of when the request was sent. See Verifying Webhook Requests for details.
network Object The ID and name (both strings) of the Ethereum network the associated transaction was submitted to. This allow your application to use the same webhook endpoint for all environments.
eventName String The name of the event. See Webhook Events for details.
eventData Varies The relevant data for this event. Usually a Codex Record, but varies by event. See Webhook Events for details.

Webhook Events

The following table lists all webhook events.

Event Name Event Data Description
codex-record:created Codex Record Sent after creating a Codex Record, when the Codex Record has been created and logged on the blockchain.
codex-record:modified Codex Record Sent after modifying a Codex Record's metadata, when the Codex Record has been updated and logged on the blockchain.
codex-coin:transferred String (amount of CODX transferred) Sent when your application receives CODX. Usually this is after requesting CODX from a testnet faucet or purchasing additional CODX on Mainnet.
codex-record:address-whitelisted Codex Record Sent after someone adds your application to a Codex Record's whitelisted addresses. This event is sent immediately as this is not an asynchronous action.
codex-record:transferred:new-owner Codex Record Sent after accepting an incoming transfer, when the Codex Record has been successfully transferred to your address and logged on the blockchain.
codex-record:transferred:old-owner Codex Record Sent after someone accepts one of your application's outgoing transfers, when the Codex Record has been successfully transferred to the recipient's address and logged on the blockchain.
codex-record:address-approved:owner Codex Record Sent after starting a transfer, when the Codex Record's approvedAddress has been updated on the blockchain and is ready to be accepted by the recipient.
codex-record:address-approved:approved Codex Record Sent after someone starts a transfer to your application, when the Codex Record's approvedAddress has been updated on the blockchain and is ready to be accepted by your application.
codex-record:address-approved:cancel Codex Record Sent after cancelling a transfer, when the Codex Record's approvedAddress has been cleared on the blockchain and can no longer be accepted by the recipient.
bulk-transaction:started Bulk Transaction Sent after creating Codex Records in bulk, when transactions start getting sent to the blockchain.
bulk-transaction:completed Bulk Transaction Sent after creating Codex Records in bulk, when all Codex Records have been created and logged on the blockchain.
bulk-transaction:claim:completed:creator Bulk Transaction Sent after someone claims your application's Bulk Transaction, when all Codex Records have been successfully transferred to the recipient's address and logged on the blockchain.
bulk-transaction:claim:completed:claim-user Bulk Transaction Sent after your application claims a Bulk Transaction, when all Codex Records have been successfully transferred to your address and logged on the blockchain.

Verifying Webhook Requests

A simple webhook implementation that verifies webhook requests from The Codex API.

import crypto from 'crypto'
import express from 'express'
import bodyParser from 'body-parser'

const app = express()

// your webhook will always be passed an application/json request
app.use(bodyParser.json())

app.post('/codex-webhook', (request, response) => {

  const { hash,  timestamp } = request.body

  // if, for some reason, your application's webhook is called without these
  //  parameters, the request most likely did not come from The Codex API and
  //  should be immediately rejected
  if (!hash || !timestamp) {
    response.sendStatus(400)
    return
  }

  // if the specified timestamp is in the future or is more than 5 minutes in
  //  the past, consider this a fraudulent request
  //
  // your "timestamp validity" window can be whatever you deem appropriate, but
  //  we recommend 5 minutes
  //
  // this is used to prevent the request from being "replayed" at a later date
  //  (via a man-in-the-middle or similar attack)
  const now = Date.now()

  if (timestamp > now || now - timestamp > 300000) {
    response.sendStatus(400)
    return
  }

  // construct the "valid hash" and compare with the hash sent by The  Codex API
  const validHash = crypto
    .createHmac('sha256', client_secret)
    .update(`${timestamp}:${client_id}`)
    .digest('hex')

  if (hash !== validHash) {
    response.sendStatus(401)
    return
  }

  // do whatever your application should do in response to this event here

  // let The Codex API know this webhook call was successful
  response.sendStatus(200)

})

Remember to replace client_id and client_secret with your application's client_id and client_secret.

The hash and timestamp properties passed with every webhook request should be used to verify that the request was actually sent by The Codex API and hasn't been spoofed by an attacker. Your application should verify the hash for all webhook events.

The verify the request, calculate the sha256 hash of the string ${timestamp}:${client_id} using your application's secret as the key. Then compare the result with the hash specified in the request.

CODX Tokens & Fees

CodexCoin (CODX) is the ERC-20 utility token used to interact with The Codex API. You must have CODX to create and edit Codex Records. See Smart Contract Addresses for Etherscan links for the CodexCoin contracts on each network.

Get CODX From Faucet

On Testnets, you can request CODX for free from the "CODX Faucet". After requesting a drip from the faucet, CODX will be sent to your account and you will receive the codex-coin:transferred webhook event upon success. You can request 1 drip every 24 hours.

See Request Faucet Drip for details on how to request CODX from the faucet.

Purchase CODX

On Mainnet, you must purchase CODX. Currently, there is no way to do this programmatically so you must login into The Codex Viewer and purchase CODX via Stripe. See Logging Into The Codex Viewer for details.

Privacy

The public nature of blockchain presents some interesting challenges when dealing with information many people people wish to keep private. To take advantage of Ethereum's distributed, public ledger while still protecting our user's private data, Codex Protocol has taken a hybrid "off-chain / on-chain" approach to storing user data.

"Off-Chain" vs "On-Chain" Data

All metadata is stored "off-chain" (i.e. in a metadata provider's database), but hashes of this data are stored on "on-chain" (i.e in a Codex Protocol smart contract.) These hashes can be used to verify the data stored in a metadata provider's database matches the information on the blockchain, and therefore has not been tampered with.

Specifically, the only "on-chain" data are hashes of the Codex Record's name, description, and associated files. Sending a request that will modify "on-chain" data is considered an asynchronous action and will eventually result in your application receiving a webhook event. See webhooks for details.

Updates to any other "off-chain" property of the Codex Record (such as the isPrivate flag, or the whitelistedAddresses array), will be immediately applied and (usually) won't cause a webhook event to be sent.

This "off-chain / on-chain" data separation is why there are two routes to modify a Codex Record. When you Modify a Codex Record, you're modifying it's "off-chain" data. When you Modify a Codex Record's Metadata, you're modifying it's "on-chain" data.

Permissions

Some properties of a Codex Record and it's metadata are not returned in API responses (i.e. they will be undefined) if the Codex Record marked as private and the requesting user does not have the appropriate permissions.

There are three levels of permissions for Codex Records:

  1. Owner - full read & write permissions, all data always returned
  2. Whitelisted Addresses - read-only permissions, most data returned
  3. Public Access / Non-Whitelisted Addresses - read-only permissions, only public information returned

Additionally, there are two levels of privacy for a Codex Record:

  1. isPrivate - indicates that the Codex Record's metadata is private and can only be retrieved by the owner or whitelisted addresses.
  2. isHistoricalProvenancePrivate - indicates whether or not the Codex Record's historical provenance should be hidden, regardless of the value of isPrivate.

Individual permissions for each property of a Codex Record and it's metadata are listed in Response Examples.

Whitelisted Addresses

Every Codex Record has an "off-chain" list of Ethereum addresses allowed to view it's private metadata. The whitelistedAddresses array allows users to provide read-only access for any address they wish. See Get a Codex Record's Whitelisted Addresses (and subsequent sections) for details on how to retrieve and update this list.

Even though private metadata is visible to whitelisted addresses, the isHistoricalProvenancePrivate flag can be used to hide the historical provenance for a Codex Record. This way, the owner can keep some (presumably more confidential) files hidden while still providing some access to others.

Approved Addresses

A Codex Record can also have an approvedAddress, which is an Ethereum address that is allowed to transfer the Codex Record to itself. This is necessary for the two-step transfer process. See Transferring Codex Records for details.

Historical Provenance

In addition to an array of images, a Codex Record may also contain a special array (files) that we call the "historical provenance". The intent is for images to contain pictures and/or videos of the item itself and for files to contain other, arbitrary files such as a PDF of physical appraisal documents or a scan of an original purchase receipt.

Since files in historical provenance may be considered more "confidential", a separate privacy control is available to keep these files hidden even when being viewed by someone with permissions to see private metadata. If a Codex Record's isHistoricalProvenancePrivate flag is true, the files array will always be visible to the owner only.

Transferring Codex Records

Transferring a Codex Record is a two-step process:

  1. The owner approves another user to accept the transfer of the Codex Record. This sets the Codex Record's approvedAddress to that user's Ethereum address, and grants them read-only access to the private metadata. At this point, the Codex Record is considered an outgoing transfer for the owner and an incoming transfer for the approved user.

  2. The approved user must then explicitly accept the incoming transfer. The approved user may also choose to ignore the transfer, in which case they remain the approvedAddress but the Codex Record can be hidden from their list of incoming transfers.

When the Codex Record is successfully transferred to the approvedAddress and logged on the blockchain, the approved user becomes the new owner of the record and a Provenance Event is logged.

Additionally, a few "off-chain" properties are reset to their default values:

Property Reset To
isIgnored false
approvedAddress null
whitelistedEmails []
whitelistedAddresses []
isHistoricalProvenancePrivate true

Proxy Users

Some applications need to perform actions on behalf of other users. This is currently limited to creating records on behalf of other addresses, and is only supported when creating Codex Records in bulk.

If you believe your application needs proxy user support, please send us an email and we'll be happy to help.

The Codex Viewer

The Codex Viewer is a frontend application written by Codex Protocol that interfaces with The Codex API. This application can be used to create, modify, and transfer Codex Records, as well as purchase CODX and browse public collections.

The Codex Viewer is available in all the same environments as the API:

Environment Network URL
Development Ropsten http://ropsten.codex-viewer.com
Staging Rinkeby https://rinkeby.codex-viewer.com
Production Mainnet https://codex-viewer.com

Logging Into The Codex Viewer

Even though OAuth2 accounts have direct API access, sometimes it's necessary to log into The Codex Viewer directly (e.g. when purchasing CODX.)

To login, simply visit the Codex Viewer in the appropriate environment (see table above) and use your application's email and client_secret as the password at the login screen.

Response Examples

An example success response (from a Get Client request):

{
  "error": null,
  "result": {
    "availableCODXBalance": 100,
    "email": "email@example.com",
    "name": "My Codex Application",
    "faucetDripLastRequestedAt": null,
    "faucetDripNextRequestAt": "2018-11-26T19:25:03.056Z"
    "address": "0x627306090abab3a6e1400e9345bc60c78a8bef57"
  }
}

All responses from The Codex API will have the same structure, regardless of whether or not the request was successful. The Codex API will always return Content-Type: application/json; charset=utf-8 response bodies with an object containing the keys error and result, one of which will always be null. Always returning a JSON body ensures you can always reliably parse responses from The Codex API as JSON, even if there's an error.

If there was error, result will be null and the error details can be found in the error object. The error object has two keys, code and message. error.code will be the same as the HTTP status header on the response, and error.message will provide an explanation of what went wrong.

Similarly, if the request was successful, error will be null and the requested data can be found in the response object.

An example error response (expired access token):

{
  "error": {
    "code": 401,
    "message": "Invalid token: access token has expired"
  },
  "result": null
}

Client

{
  "availableCODXBalance": 100,
  "email": "email@example.com",
  "name": "My Codex Application",
  "faucetDripLastRequestedAt": null,
  "faucetDripNextRequestAt": "2018-11-26T19:25:03.056Z"
  "address": "0x627306090abab3a6e1400e9345bc60c78a8bef57"
}
Property Type Description
name String The name of the application. This will be shown in The Codex Viewer as a registered application, taking the place of your application's Ethereum address in the Codex Record’s provenance.
email String The application developer’s email address. This will be used to communicate any breaking API changes or development updates.
address String The Ethereum address that identifies the application, provisioned and managed by Codex behalf of the application developer.
availableCODXBalance Number The amount of CODX your application has remaining to spend on transactions. See CODX Tokens & Fees for details.
faucetDripNextRequestAt Date (Testnets only) The time at which your application will next be able to request CODX from the faucet.
faucetDripLastRequestedAt Date (Testnets only) The time at which your application last requested CODX from the faucet.

OAuth2 Access Token

{
  "accessTokenExpiresAt": "2018-10-02T22:36:57.808Z",
  "accessToken": "d49694e5a3459759cc7ac1741de246e184e51d6e"
}
Property Type Description
accessTokenExpiresAt Date The date at which this access token will expire. See Access Token Expiration for details.
accessToken String The access token itself. This should be added to the Authorization header of all requests made to The Codex API.

Codex Record

{
  "tokenId": "0",
  "isPrivate": false,
  "isIgnored": false,
  "providerId": "codex",
  "isHistoricalProvenancePrivate": true,
  "providerMetadataId": "5bae56f75d7971becf854a72",
  "ownerAddress": "0x627306090abab3a6e1400e9345bc60c78a8bef57",
  "approvedAddress": "0x821aea9a577a9b44299b9c15c88cf3087f3b5544",
  "nameHash": "0x4955c7fd1cb2de006320e77b157409b84ab2df69d9c0720f609d7a17dd2a674c",
  "descriptionHash": "0xcb7ab38fbc9919a3b04eabc343d7e649335ad748213df20e65d1979d0d3aa800",
  "whitelistedAddresses": [
    "0xf17f52151ebef6c7334fad080c5704d77216b732",
    "0xc5fdf4076b8f3a5357c5e395ab970b5b54098fef"
  ],
  "whitelistedEmails": [
    "user@example.com"
  ],
  "fileHashes": [
    "0xb39e1bbc6d50670aafa88955217ddf1e9e1f635a2cfc8d5d806a730f014c7210"
  ],
  "provenance": [
    {
      // see " Provenance Entry" below
    }
  ],
  "metadata": {
    // see "File" below
  },
  "provider": {
    // see "Provider" below
  }
}
Property Type Permissions Description
tokenId String Public The unique identifier of this Codex Record
metadata Metadata Whitelisted Addresses (if isPrivate is true) Will be undefined if isPrivate is true and you are not the owner or a whitelisted address. See Metadata for details.
provider Metadata Provider Public See Metadata Provider for details.
nameHash String Public The hash of the metadata's plain text name.
isIgnored Boolean Public This flag indicates that a user has chosen to "ignore" an incoming Codex Record transfer. This is useful to hide incoming transfers in a User Interface since there's no blockchain mechanism to explicitly reject a transfer.
isPrivate Boolean Public This flag indicates that the metadata for this record is private and can only be retrieved by the owner, the approvedAddress, and the addresses listed in whitelistedAddresses / whitelistedEmails.
provenance Array[Provenance Event] Public An array of Provenance Events.
fileHashes Array[String] Public An array of file hashes that belong to this Codex Record's metadata.
providerId String Public See unique id of the Metadata Provider.
ownerAddress String Public The Ethereum address of the client / user that currently owns this Codex Record.
descriptionHash String Public The hash of the metadata's plain text description.
approvedAddress String Public The Ethereum address of the user currently approved to accept the transfer of this Codex Record. See Transferring a Codex Record for details. Approved addresses are considered part of the whitelistedAddresses array. See Approved Addresses for details.
providerMetadataId String Public The unique ID of the metadata that belongs to this Codex Record.
whitelistedEmails Array[String] Owner Only An array of email addresses allowed to view private metadata for this Codex Record. This allows users to give people read-only access to their Codex Records. Will be undefined if you are not the owner. See Whitelisted Addresses for details.
whitelistedAddresses Array[String] Owner Only An array of Ethereum addresses allowed to view private metadata for this Codex Record. This allows users to give people read-only access to their Codex Records. Will be undefined if you are not the owner. See Whitelisted Addresses for details.
isHistoricalProvenancePrivate Boolean Public This flag indicates whether or not historical provenance (i.e. metadata.files) should be hidden, regardless of the value of isPrivate.

Metadata

{
  "codexRecordTokenId": "0",
  "hasPendingUpdates": true,
  "id": "5bae56f75d7971becf854a72",
  "name": "Really Cool Codex Record",
  "description": "This is a really cool Codex Record!",
  "creatorAddress": "0x627306090abab3a6e1400e9345bc60c78a8bef57",
  "nameHash": "0x4955c7fd1cb2de006320e77b157409b84ab2df69d9c0720f609d7a17dd2a674c",
  "descriptionHash": "0xcb7ab38fbc9919a3b04eabc343d7e649335ad748213df20e65d1979d0d3aa800",
  "fileHashes": [
    "0xb39e1bbc6d50670aafa88955217ddf1e9e1f635a2cfc8d5d806a730f014c7210"
  ],
  "additionalMetadata": {
    // see "Additional Metadata" below
  },
  "auctionHouseMetadata": {
    // see "Auction House Metadata" below
  },
  "pendingUpdates": [
    {
      // see "Pending Update" below
    }
  ],
  "mainImage": {
    // see "File" below
  },
  "files": [
    {
      // see "File" below
    }
  ],
  "images": [
    {
      // see "File" below
    }
  ]
}

Metadata makes up all of the "off-chain" data associated with a Codex Record, e.g. the "plain text" values for the corresponding hashes stored on the blockchain. Storing this (potentially) sensitive data off-chain is necessary for privacy controls, since the nature of blockchain transactions are public.

Property Type Permissions Description
id String Public The unique ID of the metadata.
name String Whitelisted Addresses (if isPrivate is true) The plain text name of the Codex Record. Will be undefined if isPrivate is true and you are not the owner or a whitelisted address.
files Array[File] Whitelisted Addresses (if isPrivate is true and isHistoricalProvenancePrivate is false) An array of supplemental files that belong to this metadata. This is considered the "historical provenance". Can undefined if isHistoricalProvenancePrivate is true on the Codex Record. See File for details.
images Array[File] Whitelisted Addresses (if isPrivate is true) An array of supplemental images that belong to this metadata. Will be undefined if isPrivate is true and you are not the owner or a whitelisted address. See File for details.
nameHash String Public The hash of the plain text name.
mainImage File Whitelisted Addresses (if isPrivate is true) The main image of this Codex Record. Will be undefined if isPrivate is true and you are not the owner or a whitelisted address.
fileHashes Array[String] Public An array of file hashes that belong to this metadata.
description String Whitelisted Addresses (if isPrivate is true) The plain text description of the Codex Record. Will be undefined if isPrivate is true and you are not the owner or a whitelisted address.
pendingUpdates Array[Pending Update] Owner Only An array of updates that are currently being processed for this metadata. See Pending Update for details. Will be undefined if you are not the owner.
creatorAddress String Public The Ethereum address of the client / user who created this metadata (not necessarily the current Codex Record owner!)
descriptionHash String Public The hash of the plain text description.
hasPendingUpdates Boolean Public This flag is true if there is currently a "modify" transaction processing on the blockchain (e.g. after modifying a Codex Record's metadata). This is useful for showing some sort of loading state on a Codex Record while modifications are pending. See Pending Update for details.
codexRecordTokenId String Public The tokenId of the Codex Record this metadata belongs to.
additionalMetadata Object Whitelisted Addresses (if isPrivate is true) A list of additional metadata about the asset. See Additional Metadata for details.
auctionHouseMetadata Object Whitelisted Addresses (if isPrivate is true) An arbitrary list of data specific to the Auction House that created the Codex Record. See Auction House Metadata for details.

Additional Metadata

{
  "lotNumber": "123A",
  "salePrice": "1000",
  "saleCurrency": "USD",
  "editionNumber": "16",
  "totalEditions": "64",
  "condition": "Excellent",
  "dimensionsInches": "18 x 23",
  "creatorName": "Cool Guy McGee"
  "auctionName": "My Cool Auction",
  "medium": "mixed media on paper",
  "signatureLocation": "front, top left"
  "dimensionsCentimeters": "45.7 x 58.4",
  "assetSoldAt": "2019-03-21T16:30:06.030Z",
  "assetCreatedAt": "1993-01-01T00:00:00.000Z",
}

This is a list of additional properties about the asset for which the Codex Record was created. This object is intended to contain information about the physical asset such as it's condition and dimensions. This entire object and all of it's values are optional.

Property Type Description
make String The asset's make.
model String The asset's model.
medium String The asset's medium.
condition String The asset's condition.
lotNumber String The lot number of the auction this asset was part of.
auctionName String The name of the auction this asset was part of.
artistName String The name of the asset's artist.
creatorName String The name of the asset's creator.
assetSoldAt Date When the asset was sold.
salePrice String The amount in local currency the asset sold for.
saleCurrency String The local currency associated with salePrice.
editionNumber String The asset's edition number.
totalEditions String The asset's total number of editions.
assetCreatedAt Date When the asset was created.
dimensionsInches String The asset's dimensions (in inches).
signatureLocation String The location of the creator's signature.
dimensionsCentimeters String The asset's dimensions (in centimeters).

Auction House Metadata

{
  "id": "1234",
  "linkbackUrl": "https://example.com/my-auction-house/asset/1234",

  // any other arbitrary data (e.g. transactionId, inventoryNumber, artistId, etc...)

}

This is a list of arbitrary data an auction house can specify when creating a Codex Record. This object is intended to contain information specific to the auction house, such as a transaction ID, inventory number, etc.

Pending Update

{
  "id": "5bb661ab92d258fdf2efa29b",
  "name": "Super Duper New Title",
  "description": "This will replace the current description!",
  "nameHash": "0x444df25b54a53b842bb2af218e79e8cd2f1efca5c8f7a439451686fb3b825636",
  "descriptionHash": "0xcb7ab38fbc9919a3b04eabc343d7e649335ad748213df20e65d1979d0d3aa800",
  "fileHashes": [
    "0xd7bbfd8fd047f48c6724adb0633059f00f55339ed2fc96a529ef606d6ec76593"
  ],
  "mainImage": {
    // see "File" below
  },
  "files": [
    {
      // see "File" below
    }
  ],
  "images": [
    {
      // see "File" below
    }
  ]
}

Typically generated after modifying a Codex Record's metadata, a Pending Update represents the new state metadata should have after a "modify" transaction has been mined on the blockchain. This process is necessary due to the asynchronous nature of blockchain transactions.

Property Type Description
id String The unique ID of the pending update.
name String The new plain text name.
files Array[File] The new array of supplemental files.
images Array[File] The new array of supplemental images.
nameHash String The hash of the new plain text name.
mainImage File The new main image.
fileHashes Array[String] An array of file hashes representing all the new files & images.
description String The new plain text description.
descriptionHash String The hash of the new plain text description.

Provenance Event

{
  "type": "transferred",
  "oldOwnerAddress": "0x627306090abab3a6e1400e9345bc60c78a8bef57",
  "newOwnerAddress": "0xf17f52151ebef6c7334fad080c5704d77216b732",
  "transactionHash": "0x0a3a518d1a0786cbdc6265dc24fc0112411c996363465a2789dc3d8021cd4c44",
}

A Provenance Event is a record of a blockchain transaction related to a specific Codex Record. A Codex Record's provenance array is essentially a transaction ledger, detailing when it was created, who it was transferred to, and who modified what data.

Property Type Description
type String One of ['created', 'modified', 'transferred'].
oldOwnerAddress String The Ethereum address of the owner before this transaction occurred. For created events, this will be the "zero address" (a special address that nobody can own, used as a "null value" for address-type variables.) For modified events, this will be the same as newOwnerAddress.
newOwnerAddress String The Ethereum address of the after before this transaction occurred. For modified events, this will be the same as oldOwnerAddress.
transactionHash String The Ethereum transaction hash of the transaction that emitted this event.

Metadata Provider

{
  "id": "codex",
  "description": "The Codex API",
  "metadataUrl": "https://rinkeby-api.codexprotocol.com/v1/records/"
}

A Metadata Provider is known source of Codex Record metadata. Since metadata must be stored "off-chain", Metadata Providers are essentially databases / API endpoints which can be used to retrieve Codex Record metadata.

Property Type Description
id String The unique ID of the metadata provider.
description String A description of the metadata provider.
metadataUrl String The URL to request Codex Record Metadata from. The Codex Record's tokenId should be appended to this URL to retrieve metadata about that record.

File

{
  "width": "1024",
  "size": "181516",
  "height": "1024",
  "fileType": "image",
  "name": "cool-file.jpg",
  "mimeType": "image/jpeg",
  "id": "5bae56f65d7971becf854a71",
  "creatorAddress": "0x627306090abab3a6e1400e9345bc60c78a8bef57",
  "hash": "0xb39e1bbc6d50670aafa88955217ddf1e9e1f635a2cfc8d5d806a730f014c7210",
  "uri": "https://s3.amazonaws.com/codex.registry/files/1538152180696.c8a9ec69-0ed8-4516-a34c-e15ebbe4749c.jpg"
}

When files are uploaded to The Codex API, they are processed to determine useful bits of information such as width, height, and size (in bytes). The are then uploaded to S3, and the resulting information is stored in the The Codex API database.

Property Type Description
id String The unique ID of the file.
uri String The URL of the file.
size String The size (in bytes) of the file.
name String The name of the file, as passed in when created.
hash String The hash of the file data.
width String The width (in pixels) of the file, if it is an image. Otherwise null.
height String The height (in pixels) of the file, if it is an image. Otherwise null.
fileType String One of ['file', 'video', or 'document']. Useful when displaying this file on a web page.
mimeType String The MIME type of the file.
creatorAddress String The Ethereum address of the client / user created this file (not necessarily the current Codex Record owner!)

Bulk Transaction

{
  "id": "5c9162551a54697e9331d88b",
  "claimUrl": "https://codex-viewer.com/claim-records/4ACsjkrR",
  "claimStatus": "claimed",
  "claimCode": "4ACsjkrR",
  "type": "record-mint",
  "status": "complete",
  "recordMintData": {
    "numCreated": 3,
    "numClaimed": 3,
    "insertionErrors": [
      "name: Path `name` is required."
    ],
    "existingAuctionHouseUniqueIds": [
      "1234"
    ]
  }
}

In this example, 5 metadata objects were specified when creating Codex Records in bulk, one of which failed due to a missing name parameter and another failed due to a duplicate auctionHouseMetadata.id already in The Codex API database for your application.

When bulk routes are are called (e.g. when creating Codex Records in bulk), a Bulk Transaction object is returned. This object can be used to monitor the progress of an in-progress Bulk Transaction, or to retrieve stats about an already-completed Bulk Transaction such as how many Codex Records were created.

Property Type Description
id String The unique ID of the Bulk Transaction.
type String The type of Bulk Transaction, currently the only value this can have is record-mint.
status String One of the following created, pending, or complete. "Created" means the Bulk Transaction is in the queue waiting to be processed, "pending" means it's in progress, and "completed" means the entire Bulk Transaction is done.
claimUrl String If generateClaimCode was specified when creating the Bulk Transaction, this is the url which can be used to claim this Bulk Transaction via The Codex Viewer.
claimCode String If generateClaimCode was specified when creating the Bulk Transaction, this is the code which can be used to claim this Bulk Transaction via The Codex API.
claimStatus String One of the following unclaimed, pending, or claimed. "Unclaimed" means the Bulk Transaction is waiting to be claimed, "pending" means it's currently transferring Codex Records to the claiming user, and "claimed" means the claim process is complete.
recordMintData[numCreated] Number If this Bulk Transaction is of type record-mint, this is the number of Codex Records successfully created. This value is only updated when the status transitions from pending to complete.
recordMintData[numClaimed] Number If this Bulk Transaction is of type record-mint, and generateClaimCode was specified when creating the Bulk Transaction, this is the number of Codex Records successfully transferred to the claiming user. This value is only updated when the claimStatus transitions from pending to claimed.
recordMintData[insertionErrors] Array[String] If this Bulk Transaction is of type record-mint, this is a list of errors that occurred while inserting the soon-to-be-created metadata records.
recordMintData[existingAuctionHouseUniqueIds] Array[String] If this Bulk Transaction is of type record-mint, this is a list of the specified auctionHouseMetadata.id values that correspond to an already-existing Codex Record in The Codex API database for your application. (Duplicates are not created.)

Errors

The Codex API can return the following error codes:

Error Code Error Description
401 Unauthorized Error - Your access token is invalid (e.g. it has expired and you need to request a new one.)
402 Payment Required Error - You do not have enough CODX to perform the requested action. On testnets, you may request more CODX from the faucet. On Mainnet, you must purchase additional CODX.
403 Forbidden Error - Your access token is valid, but you do not have permission to perform the request action (e.g. "admin only" actions.)
404 Not Found Error - The requested resource (or route) does not exist.
409 Missing Parameter Error - A required parameter was not specified in the request data. See error message for details.
409 Invalid Argument Error - An invalid parameter was specified in the request data (e.g. an invalid email address), or the requested action can not be performed (e.g. cancelling a transfer that hasn't been initiated yet.) See error message for details.
409 Conflict Error - The requested resource could not be created because a record with a matching unique identifier already exists. See error message for details.
500 Internal Server Error - There was an unknown problem processing the request. Please try again later.
503 Service Unavailable - We're temporarily offline for maintenance. Please try again later.

Blockchain Details

This section contains details about the blockchain specifics of Codex Protocol. It's geared towards developers who are building blockchain-based applications that interface directly with our smart contracts, or who are building NFT wallets and want to correctly display Codex Records.

Smart Contract Addresses

The following table lists the contracts for each environment we deploy to. Click a link to check out that contract on Etherscan.

Ropsten Rinkeby Mainnet
CodexCoin CodexCoin CodexCoin
CodexRecord CodexRecord CodexRecord
CodexRecordProxy CodexRecordProxy CodexRecordProxy

Listening to Contract Events

Listening for events via the CodexRecordProxy:


import Web3 from 'web3'
const web3 = new Web3(process.env.ETHEREUM_RPC_URL)

const codexRecordProxyAddress = /* see table above */
const codexRecordABI = /* see: https://raw.githubusercontent.com/codex-protocol/npm.ethereum-service/master/static/contracts/1/CodexRecord.json */

const CodexRecord = new web3.eth.Contract(codexRecordABI, codexRecordProxyAddress)

// @NOTE: this is just an example, this would likely pull way too many events so
//  you'd need to request event in chunks
CodexRecord
  .getPastEvents('allEvents', { fromBlock: 6000000, toBlock: 'latest' })
  .then((events) => {
    // see https://web3js.readthedocs.io/en/1.0/web3-eth-contract.html?highlight=events#getpastevents
  })

To get CodexRecord events, you must specify the ABI of the CodexRecord contract but the address of the CodexRecordProxy contract.

If you wish to subscribe to CodexRecord contract events, please note that events are emitted from the CodexRecordProxy contract address and NOT the CodexRecord contract.

Get ERC-721 Token Metadata

Get a token's metadata for display in an Ethereum wallet with NFT support:

import request from 'request'

const options = {
  url: 'https://rinkeby-api.codexprotocol.com/token-metadata/0',
  method: 'get',
  json: true,
}

request(options, (error, response) => {
  console.log(response.body) // ERC-721 Metadata JSON for Codex Record with tokenId 0
})

The above API call returns ERC-721 Metadata JSON, e.g.

{
  "name": "Really Cool Codex Record",
  "description": "This is a really cool Codex Record!",
  "image": "https://s3.amazonaws.com/codex.registry/files/1538152180696.c8a9ec69-0ed8-4516-a34c-e15ebbe4749c.jpg"
}

Our smart contract implements the optional ERC-721 metadata extension, which defines a unique tokenURI for each token in the registry. Since token metadata is dynamic, the tokenURIs point to an endpoint in our API instead of a static file on IPFS.

HTTP Request

GET /token-metadata/:tokenId

URL Parameters

Parameter Type Description
tokenId Number The tokenId of the Codex Record for which to retrieve the metadata of.