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. Additionally, we only support the client_credentials grant, which means that calls to The Codex API must be from a confidential client. A confidential client is an application that can protect secrets from public users (i.e. calls to The Codex API should be happening on a server application and not from within the browser.)

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 gas allowance before sending a request that would consume gas.

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.

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: {
    isPrivate: false,
    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'),
    ],
  },
}

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

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.

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

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

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.

Gas Allowance

Since each blockchain transaction The Codex API sends on behalf of your application cost small amounts of Ether (known as "gas"), we limit all applications to a certain amount of gas per period. This ensures The Codex API is not abused.

At the start of each period, your gas allowance is reset. The following table shows the reset periods for each environment:

Network Period
Ropsten Every 24 hours
Rinkeby Every 30 days
Mainnet Every 30 days

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
isPrivate true
isIgnored false
isInGallery false
approvedAddress null
whitelistedEmails []
whitelistedAddresses []
isHistoricalProvenancePrivate true

Response Examples

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

{
  "error": null,
  "result": {
    "gasAllowance": "40500000",
    "email": "email@example.com",
    "name": "My Codex Application",
    "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

{
  "gasAllowance": "40500000",
  "email": "email@example.com",
  "name": "My Codex Application",
  "gasAllowanceRemaining": "28160450",
  "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.
gasAllowance String The total amount of gas the application can spend until the gas allowance is reset (this is what gasAllowanceRemaining is reset to every period.) See Gas Allowance for details.
gasAllowanceRemaining String The amount of gas left that the application can spend until it resets. See Gas Allowance for details.

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,
  "isInGallery": 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"
  ],
  "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.

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!)

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 gas allowance to perform the requested action. Wait for your allowance to reset, or purchase more allowance.
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
CodexStakeContract CodexStakeContract CodexStakeContract
IdentityPlatform IdentityPlatform N/A

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.