Otterspace Docs



While our protocol is fully open, all communications with our API will require authentication. If you're more interested in just reading the data to build something, you can use our subgraphs.
A Raft token (NFT) is the authorization token needed to create badges withing your account. Please fill out this form to apply for a Raft token and one of our team members will contact you ASAP and help you get setup.

Integration types

Server-side integrations

Once you have obtained a Raft Token, you can generate an API key via the settings page of your Raft.

Generating an API Key

  • Sign in to and navigate to your Raft's settings page via the drop-down menu.
  • Click the edit icon to access Raft settings.
  • Go to "API" tab
  • Enter a name for your app and then click "Generate key"
  • Note that this key is only viewable/copy-able only once i.e., upon generation. If you lose it, you would need to generate a new one and delete the old one
IMPORTANT: you should do everything to secure your API key and it should be easily accessible to exposed unauthorized parties
Your raft's API settings page
Note that only the API routes under /external are accessible with this API key for server side integrations. You will need to provide a header ‘Authorization’ that contains an API key as a Base64 string. Using the raw string of the API key from the UI will not work, unless it is base64 encoded. You can use this tool to help convert it.
curl "" \
-H "Authorization: <API key as base64 encoded string>"

Client-side integrations

If you're building a client-side integration i.e., a web application without a server-side componenet for instance, you will need to use a different authentication flow called SIWE or Sign-in With Ethereum.
Note: API access for client-side integrations not fully open. Please contact us via email or discord to discuss your requirements and your access.

Step 1: Retrieve a nonce

$ curl -X GET "" \
-H "Content-Type: application/json"
{ "nonce": "<random nonce>" }

Step 2: Sign-in

$ curl -X POST "" \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-d '{ "signature": "<signed message>", "message": "<message object>" }'
{ "<siweMessage>" }
HTTP/1.1 200 OK
Date: Mon, 18 Aug 2022 13:08:53 GMT
Set-Cookie: "<created cookie for this specific user>"
message field in the request is composed standardised off-chain authentication message proposed in EIP-4361. It is an object that have certain fields like address, chainId, nonce, domain etc. This standardised message is of type SiweMessage from the siwe library.
const siweMessage = new siwe.SiweMessage({
domain: "<string>",
address: "<string>",
statement: "<string>",
uri: "<string>",
version: "<string>",
nonce: '<received nonce from previous step>',
issuedAt: "<YYYY-MM-DDTHH:MM:SS.000Z>",
chainId: "<number>",
signature field in the request body is signed version of the message created by the wallet of the user. Simply put a message is signed by the user's private key and then signature is verified on the API side using user's public key. Concept is called elliptic curve cryptography. For a programmatic message signing please check the section below.
IMPORTANT: For authentication of further requests to API please provide Cookie HTTP Header. You can extract the cookie from response’s header Set-Cookie.

✍🏼 Programmatic message generation and signing with siwe/ethers.js

// import necessary dependencies
import {
} from "ethers";
import {
} from "siwe";
async function main() {
// provide a private key for your existing wallet (you can also create a random one: Wallet.createRandom())
const wallet = new Wallet(privateKey);
// generate a Siwe message
const siweMsg = new SiweMessage({
domain: "<>", // RFC 4501 dns authority that is requesting the signing.
wallet.address, // wallet address of the user
statement: "<>", // Human-readable ASCII assertion that the user will sign,
uri: "<>", // RFC 3986 URI referring to the resource that is the subject of the signing
version: "1", // Current version of the message.
chainId: 10, // EIP-155 Chain ID to which the session is bound, and the network where Contract Accounts must be resolved.
nonce, // the nonce you acquired
issuedAt: new Date().toISOString(),
// parse all the fields in the object and creates a sign message according with the type defined
const message = siweMsg.prepareMessage();
// sign the message with your wallet
const signature = await wallet.signMessage(message.toString());
return {
signature: signature,
message: message