Code API
The Code API is a gRPC API service designed to handle various actions such as creating Payment Requests
, Login With Code
, checking intent status, and more. Being language-agnostic, you can use any programming language that supports gRPC to interact with the Code API.
If you require a feature that isn't yet supported by our SDKs, you can directly call the Code API. This approach is particularly useful if you're working with an unsupported language or need to implement something more custom.
Recommendation
When possible, we encourage using our SDKs to interact with the Code API. The SDKs manage the low-level details of constructing the binary blob that represents the payment Intent and signing it with the rendezvous key.
Getting Started
To begin using the Code API, follow these steps:
- Familiarize yourself with Protobuf, the serialization format used by the Code API.
- Locate the Protobuf Definition for the message you intend to send.
- Construct the binary blob representing the message.
- Sign the binary blob with your rendezvous key.
- Send the signed binary blob to the Code API.
The Code API supports a wide range of messages, each with its own Protobuf definition for request and response structures.
Protobuf
We use Protocol Buffers (protobuf) to define the structure of messages sent to and received from the Code API.
A protobuf definition is a way to define the structure of a message; simply a text file with a .proto
extension. This file can be compiled into source code for many different languages. These can then be used to serialize and deserialize messages into a binary format (more compact).
syntax = "proto3";
message Person {
string name = 1; // Field number 1
int32 age = 2; // Field number 2
}
syntax = "proto3";
message Person {
string name = 1; // Field number 1
int32 age = 2; // Field number 2
}
All the protobuf definitions for the Code API can be found here. Nearly every language has a protobuf library that can be used to construct the binary blob. You can also construct the binary blob by hand.
TIP
You can play around with the Protobuf Playground to get a feel for how it works.
If you're unsure about the content of a signed message, how to construct it, where to submit it, or how to verify it, please take a look at the TypeScript intents. These are the most up-to-date and accurate representations of the signed messages.
gRPC
While we use protobuf
to define the structure of messages, we use gRPC
to send and receive these messages. The gRPC protocol is a high-performance, open-source, universal RPC framework. It is designed and maintained by Google.
We make use of gRPC tooling that generates client and server code in multiple languages, making it easy to interact with the Code API.
Web Proxy
Typically, gRPC requires a direct socket connection to the server. However, this can be difficult to set up in some environments. Specifically, it can be challenging to use gRPC in a browser environment.
We provide a custom HTTP/1.1
gRPC proxy that you can use to interact with the Code API without needing to open a socket connection. This is far easier than opening a socket connection and is the recommended way to interact with the Code API. You can also run the proxy on your own server if you'd like.
Additionally, the proxy is available over HTTP/1.1, which means you can use it in a browser environment.
Recommendation
We recommend using the gRPC proxy to interact with the Code API. It allows you to use simple fetch(...)
requests to interact with the API instead of opening a socket connection.
Mainnet Endpoints
You can use the following URLs to interact with the Code API:
Proxy URL (HTTP/1.1)
https://cash.getcode.com/v1/api
https://cash.getcode.com/v1/api
Or you can use the direct gRPC URL, which does require a grpc socket connection.
Direct gRPC URL (HTTP/2)
https://api.codeinfra.net
https://api.codeinfra.net
Building Your Own Client
When the SDK doesn’t yet support a feature you need, you can build your own client to interact directly with the Code API. In this section, we explain the Code API envelope protocol
and provide minimal examples using JavaScript, Python, PHP, and Go.
Request/Response Protocol (HTTP/1.1)
The Code API wraps your inner messages (such as a payment or login Intent) in a simple envelope defined using Protocol Buffers. If you're using our HTTP/2
endpoint, you can skip this section.
The proto definitions are:
syntax = "proto3";
message Request {
string version = 1;
string service = 2;
string method = 3;
bytes body = 4;
}
message Response {
Result result = 1;
bytes body = 2;
string message = 3;
enum Result {
OK = 0;
ERROR = 1;
}
}
syntax = "proto3";
message Request {
string version = 1;
string service = 2;
string method = 3;
bytes body = 4;
}
message Response {
Result result = 1;
bytes body = 2;
string message = 3;
enum Result {
OK = 0;
ERROR = 1;
}
}
Details
For example, the following Request
:
{
"version": "1.0",
"service": "Messaging",
"method": "sendMessage",
"body": [1, 2, 3, 4]
}
{
"version": "1.0",
"service": "Messaging",
"method": "sendMessage",
"body": [1, 2, 3, 4]
}
- version: The API version (e.g.
"1.0"
). - service: The target service (e.g.
"Messaging"
). - method: The method to invoke (e.g.
"sendMessage"
). - body: The binary-serialized payload of your inner message.
Serializes to the following hex string:
0a03312e3012094d6573736167696e671a0b73656e644d657373616765220401020304
0a03312e3012094d6573736167696e671a0b73656e644d657373616765220401020304
Sending a Request
Below is a minimal JavaScript example that wraps your protobuf-serialized payload in a Request
envelope and sends it via HTTP/1.1
POST.
import { Request, Response } from '@code-wallet/rpc';
// Assume you already have your payload message
// (e.g. a payment or login Intent) that has been
// encoded (and signed) using its protobuf definition.
const payload = myProtoMessage.toBinary();
// Wrap the payload in a Request envelope
const envelope = new Request({
version: "1.0",
service: "Intents", // e.g. "Messaging"
method: "sendMessage", // e.g. "sendMessage"
body: payload,
});
// Serialize the envelope to binary
const binaryEnvelope = envelope.toBinary();
// Send the binary envelope using an HTTP POST
fetch("https://cash.getcode.com/v1/api", {
method: "POST",
headers: { "Content-Type": "application/octet-stream" },
body: binaryEnvelope,
})
.then(response => response.arrayBuffer())
.then(buffer => {
// Decode the response envelope from binary
const resEnvelope = Response.fromBinary(new Uint8Array(buffer));
console.log("Received response:", resEnvelope);
})
.catch(err => console.error("Network error:", err));
import { Request, Response } from '@code-wallet/rpc';
// Assume you already have your payload message
// (e.g. a payment or login Intent) that has been
// encoded (and signed) using its protobuf definition.
const payload = myProtoMessage.toBinary();
// Wrap the payload in a Request envelope
const envelope = new Request({
version: "1.0",
service: "Intents", // e.g. "Messaging"
method: "sendMessage", // e.g. "sendMessage"
body: payload,
});
// Serialize the envelope to binary
const binaryEnvelope = envelope.toBinary();
// Send the binary envelope using an HTTP POST
fetch("https://cash.getcode.com/v1/api", {
method: "POST",
headers: { "Content-Type": "application/octet-stream" },
body: binaryEnvelope,
})
.then(response => response.arrayBuffer())
.then(buffer => {
// Decode the response envelope from binary
const resEnvelope = Response.fromBinary(new Uint8Array(buffer));
console.log("Received response:", resEnvelope);
})
.catch(err => console.error("Network error:", err));
import requests
# We don't yet have a Python library for encoding/decoding
# the CodeAPI protobuf messages.
# However, you can build your own.
# More details here: https://protobuf.dev/getting-started/pythontutorial/
# In this example, we're just going to do it
# manually (not recommended but simple).
def encode_varint(value):
"""Encode an integer as a protobuf varint."""
result = bytearray()
while True:
to_write = value & 0x7F
value >>= 7
if value:
result.append(to_write | 0x80)
else:
result.append(to_write)
break
return bytes(result)
def encode_length_delimited(field_number, data_bytes):
"""
Encode a length-delimited field.
For proto fields of type string or bytes:
- Tag: (field_number << 3) | 2 (wire type 2)
- Length: varint-encoded length of data_bytes
- Value: the raw bytes
"""
tag = (field_number << 3) | 2
return encode_varint(tag) + encode_varint(len(data_bytes)) + data_bytes
# Prepare the fields for the Request message.
version = "1.0".encode("utf-8")
service = "Messaging".encode("utf-8")
method = "sendMessage".encode("utf-8")
body = bytes([1, 2, 3, 4]) # Example payload
# Manually serialize the Request message by concatenating each field.
serialized = (
encode_length_delimited(1, version) +
encode_length_delimited(2, service) +
encode_length_delimited(3, method) +
encode_length_delimited(4, body)
)
# The expected hex serialization:
# 0a03312e3012094d6573736167696e671a0b73656e644d657373616765220401020304
print("Serialized Request:", serialized.hex())
# Send the serialized request via HTTP POST.
response = requests.post(
"https://cash.getcode.com/v1/api",
headers={"Content-Type": "application/octet-stream"},
data=serialized
)
if response.ok:
# For demonstration, print the raw response bytes in hex.
print("Response:", response.content.hex())
else:
print("Request failed with status code:", response.status_code)
import requests
# We don't yet have a Python library for encoding/decoding
# the CodeAPI protobuf messages.
# However, you can build your own.
# More details here: https://protobuf.dev/getting-started/pythontutorial/
# In this example, we're just going to do it
# manually (not recommended but simple).
def encode_varint(value):
"""Encode an integer as a protobuf varint."""
result = bytearray()
while True:
to_write = value & 0x7F
value >>= 7
if value:
result.append(to_write | 0x80)
else:
result.append(to_write)
break
return bytes(result)
def encode_length_delimited(field_number, data_bytes):
"""
Encode a length-delimited field.
For proto fields of type string or bytes:
- Tag: (field_number << 3) | 2 (wire type 2)
- Length: varint-encoded length of data_bytes
- Value: the raw bytes
"""
tag = (field_number << 3) | 2
return encode_varint(tag) + encode_varint(len(data_bytes)) + data_bytes
# Prepare the fields for the Request message.
version = "1.0".encode("utf-8")
service = "Messaging".encode("utf-8")
method = "sendMessage".encode("utf-8")
body = bytes([1, 2, 3, 4]) # Example payload
# Manually serialize the Request message by concatenating each field.
serialized = (
encode_length_delimited(1, version) +
encode_length_delimited(2, service) +
encode_length_delimited(3, method) +
encode_length_delimited(4, body)
)
# The expected hex serialization:
# 0a03312e3012094d6573736167696e671a0b73656e644d657373616765220401020304
print("Serialized Request:", serialized.hex())
# Send the serialized request via HTTP POST.
response = requests.post(
"https://cash.getcode.com/v1/api",
headers={"Content-Type": "application/octet-stream"},
data=serialized
)
if response.ok:
# For demonstration, print the raw response bytes in hex.
print("Response:", response.content.hex())
else:
print("Request failed with status code:", response.status_code)
# We don't yet have a Php library for encoding/decoding
# the CodeAPI protobuf messages.
# In this example, we're just going to do it
# manually (not recommended but simple).
function encodeVarint($value) {
$result = '';
while (true) {
$toWrite = $value & 0x7F;
$value >>= 7;
if ($value) {
$result .= chr($toWrite | 0x80);
} else {
$result .= chr($toWrite);
break;
}
}
return $result;
}
function encodeLengthDelimited($fieldNumber, $dataBytes) {
$tag = ($fieldNumber << 3) | 2;
return encodeVarint($tag) . encodeVarint(strlen($dataBytes)) . $dataBytes;
}
// Prepare the fields for the Request message.
$version = "1.0";
$service = "Intents";
$method = "createIntent";
$body = chr(1) . chr(2) . chr(3) . chr(4); // Example payload
// Manually serialize the Request message.
$serialized =
encodeLengthDelimited(1, $version) .
encodeLengthDelimited(2, $service) .
encodeLengthDelimited(3, $method) .
encodeLengthDelimited(4, $body);
echo "Serialized Request: " . bin2hex($serialized) . "\n";
// Send the serialized request via HTTP POST using cURL.
$ch = curl_init("https://cash.getcode.com/v1/api");
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, array("Content-Type: application/octet-stream"));
curl_setopt($ch, CURLOPT_POSTFIELDS, $serialized);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
if (curl_errno($ch)) {
echo "Request failed: " . curl_error($ch) . "\n";
} else {
echo "Response: " . bin2hex($response) . "\n";
}
curl_close($ch);
# We don't yet have a Php library for encoding/decoding
# the CodeAPI protobuf messages.
# In this example, we're just going to do it
# manually (not recommended but simple).
function encodeVarint($value) {
$result = '';
while (true) {
$toWrite = $value & 0x7F;
$value >>= 7;
if ($value) {
$result .= chr($toWrite | 0x80);
} else {
$result .= chr($toWrite);
break;
}
}
return $result;
}
function encodeLengthDelimited($fieldNumber, $dataBytes) {
$tag = ($fieldNumber << 3) | 2;
return encodeVarint($tag) . encodeVarint(strlen($dataBytes)) . $dataBytes;
}
// Prepare the fields for the Request message.
$version = "1.0";
$service = "Intents";
$method = "createIntent";
$body = chr(1) . chr(2) . chr(3) . chr(4); // Example payload
// Manually serialize the Request message.
$serialized =
encodeLengthDelimited(1, $version) .
encodeLengthDelimited(2, $service) .
encodeLengthDelimited(3, $method) .
encodeLengthDelimited(4, $body);
echo "Serialized Request: " . bin2hex($serialized) . "\n";
// Send the serialized request via HTTP POST using cURL.
$ch = curl_init("https://cash.getcode.com/v1/api");
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, array("Content-Type: application/octet-stream"));
curl_setopt($ch, CURLOPT_POSTFIELDS, $serialized);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
if (curl_errno($ch)) {
echo "Request failed: " . curl_error($ch) . "\n";
} else {
echo "Response: " . bin2hex($response) . "\n";
}
curl_close($ch);
package main
import (
"bytes"
"encoding/hex"
"fmt"
"io/ioutil"
"net/http"
)
// You can use our library to encode/decode the CodeAPI protobuf messages.
// https://github.com/code-payments/code-protobuf-api/tree/main/generated/go
// We're going to do it manually (not recommended but simple).
func encodeVarint(value uint64) []byte {
var buf []byte
for {
b := byte(value & 0x7F)
value >>= 7
if value != 0 {
buf = append(buf, b|0x80)
} else {
buf = append(buf, b)
break
}
}
return buf
}
func encodeLengthDelimited(fieldNumber int, data []byte) []byte {
tag := uint64((fieldNumber << 3) | 2)
var buf bytes.Buffer
buf.Write(encodeVarint(tag))
buf.Write(encodeVarint(uint64(len(data))))
buf.Write(data)
return buf.Bytes()
}
func main() {
version := []byte("1.0")
service := []byte("Intents")
method := []byte("createIntent")
body := []byte{1, 2, 3, 4} // Example payload
var serialized bytes.Buffer
serialized.Write(encodeLengthDelimited(1, version))
serialized.Write(encodeLengthDelimited(2, service))
serialized.Write(encodeLengthDelimited(3, method))
serialized.Write(encodeLengthDelimited(4, body))
serializedBytes := serialized.Bytes()
fmt.Println("Serialized Request:", hex.EncodeToString(serializedBytes))
// Send HTTP POST
resp, err := http.Post(
"https://cash.getcode.com/v1/api",
"application/octet-stream",
bytes.NewReader(serializedBytes)
)
if err != nil {
fmt.Println("Request failed:", err)
return
}
defer resp.Body.Close()
responseBody, _ := ioutil.ReadAll(resp.Body)
fmt.Println("Response:", hex.EncodeToString(responseBody))
}
package main
import (
"bytes"
"encoding/hex"
"fmt"
"io/ioutil"
"net/http"
)
// You can use our library to encode/decode the CodeAPI protobuf messages.
// https://github.com/code-payments/code-protobuf-api/tree/main/generated/go
// We're going to do it manually (not recommended but simple).
func encodeVarint(value uint64) []byte {
var buf []byte
for {
b := byte(value & 0x7F)
value >>= 7
if value != 0 {
buf = append(buf, b|0x80)
} else {
buf = append(buf, b)
break
}
}
return buf
}
func encodeLengthDelimited(fieldNumber int, data []byte) []byte {
tag := uint64((fieldNumber << 3) | 2)
var buf bytes.Buffer
buf.Write(encodeVarint(tag))
buf.Write(encodeVarint(uint64(len(data))))
buf.Write(data)
return buf.Bytes()
}
func main() {
version := []byte("1.0")
service := []byte("Intents")
method := []byte("createIntent")
body := []byte{1, 2, 3, 4} // Example payload
var serialized bytes.Buffer
serialized.Write(encodeLengthDelimited(1, version))
serialized.Write(encodeLengthDelimited(2, service))
serialized.Write(encodeLengthDelimited(3, method))
serialized.Write(encodeLengthDelimited(4, body))
serializedBytes := serialized.Bytes()
fmt.Println("Serialized Request:", hex.EncodeToString(serializedBytes))
// Send HTTP POST
resp, err := http.Post(
"https://cash.getcode.com/v1/api",
"application/octet-stream",
bytes.NewReader(serializedBytes)
)
if err != nil {
fmt.Println("Request failed:", err)
return
}
defer resp.Body.Close()
responseBody, _ := ioutil.ReadAll(resp.Body)
fmt.Println("Response:", hex.EncodeToString(responseBody))
}
The above examples show how to construct a Request
envelope and send it to the Code API. The response is also wrapped in a Response
envelope. You can then extract the binary-encoded payload from the response and decode it using the appropriate protobuf definition.
You can learn more about the payload structure below.
Recommendation
Take a look at the following client implementations that use our HTTP/1.1
gRPC proxy.
HTTP/2 gRPC Endpoint
Additionally, you can also use our HTTP/2
gRPC endpoint directly. This requires a gRPC client library that supports gRPC over HTTP/2
.
Our golang, iOS and Android SDKs use this method.
INFO
Take a look at the following client implementations that use our HTTP/2
gRPC endpoint directly.
Intents
Code uses the concept of an Intent to represent a user's desired actions. This is different from a blockchain transaction. An Intent is a higher-level abstraction.
The Code Sequencer expects a binary blob that represents the Intent. The binary blob is constructed using the protobuf definition. When you call the createIntent
API, you'll need to construct the binary blob yourself and sign it with the rendezvous key.
We have several Intent types, such as RequestToReceiveBill
, RequestToPayBill
, and RequestToLogin
. Each Intent type has its own protobuf definition.
Details
At a high level, the Intent structure looks like this:
message Message {
MessageId id = 1;
Signature send_message_request_signature = 3;
oneof kind {
RequestToGrabBill request_to_grab_bill = 2;
RequestToReceiveBill request_to_receive_bill = 5;
CodeScanned code_scanned = 6;
ClientRejectedPayment client_rejected_payment = 7;
IntentSubmitted intent_submitted = 8;
WebhookCalled webhook_called = 9;
RequestToLogin request_to_login = 10;
ClientRejectedLogin client_rejected_login = 12;
AirdropReceived airdrop_received = 4;
}
}
message Message {
MessageId id = 1;
Signature send_message_request_signature = 3;
oneof kind {
RequestToGrabBill request_to_grab_bill = 2;
RequestToReceiveBill request_to_receive_bill = 5;
CodeScanned code_scanned = 6;
ClientRejectedPayment client_rejected_payment = 7;
IntentSubmitted intent_submitted = 8;
WebhookCalled webhook_called = 9;
RequestToLogin request_to_login = 10;
ClientRejectedLogin client_rejected_login = 12;
AirdropReceived airdrop_received = 4;
}
}
The SDKs are basically wrappers around this protobuf definition. They construct the binary blob for you and sign it with the rendezvous key. If you'd like to use a custom backend or a language that isn't supported yet, it is possible to call our createIntent
API directly.
Recommendation
Take a look at the internals of the SDKs to see how the binary blob is constructed and signed. This will give you a good idea of how to construct the binary blob yourself.
Below are some examples of how to construct the binary blob for a payment Intent and a login Intent. These examples are based on the protobuf definitions provided earlier. You'll need to sign and wrap these payloads in a Request
envelope before sending them to the Code API.
GetStatus Example
In this example, we’ll query the status of an intent by sending a GetStatusRequest
message. This service is simple because it doesn’t require a signature.
We will:
- Encode a
GetStatusRequest
message. - Wrap it in a
Request
envelope. - Save the binary payload to a file.
- Use
cURL
to send the request. - Interpret the response.
All without using any SDKs or writing any code (JavaScript version is here).
Recommendation
We don't recommend constructing the binary blob manually or by hand unless you have a specific reason to do so. It's error-prone and can lead to unexpected behavior. Use a protobuf library to construct the binary blob.
Details
First, here are the relevant protobuf definitions. We will use the protobuf playground to encode and decode the messages.
syntax = "proto3";
message IntentId {
bytes value = 1;
}
message GetStatusRequest {
IntentId intent_id = 1;
}
message GetStatusResponse {
bool exists = 1;
bool code_scanned = 2;
bool intent_submitted = 3;
}
message Request {
string version = 1;
string service = 2;
string method = 3;
bytes body = 4;
}
message Response {
Result result = 1;
bytes body = 2;
string message = 3;
enum Result {
OK = 0;
ERROR = 1;
}
}
syntax = "proto3";
message IntentId {
bytes value = 1;
}
message GetStatusRequest {
IntentId intent_id = 1;
}
message GetStatusResponse {
bool exists = 1;
bool code_scanned = 2;
bool intent_submitted = 3;
}
message Request {
string version = 1;
string service = 2;
string method = 3;
bytes body = 4;
}
message Response {
Result result = 1;
bytes body = 2;
string message = 3;
enum Result {
OK = 0;
ERROR = 1;
}
}
Step 1: Convert the Base58 Intent ID to a Byte Array
Lets assume we made a payment in the past using the Code app and have a Intent id. Our intent ID is given in Base58 as: 9sK9x3MC7yEMzfA5VRRcu2P72gCGGsF1htpeAMXae9bz
Details
You can convert this Base58 string to a byte array without writing any code by using an online converter:
Visit an online Base58 converter: For example, try the AppDevTools Base58 Encoder/Decoder.
Paste the Base58 string: Enter
9sK9x3MC7yEMzfA5VRRcu2P72gCGGsF1htpeAMXae9bz
in the input field.Convert to Hexadecimal: The tool will decode the Base58 string and output its hexadecimal representation.
Convert Hex to a Decimal Array. Copy the hexadecimal string and convert it to a decimal array using the following command:
echo "83c110200748f37e98f0ae36802eebcc605f023a1661f3731c3620b470c78325" \
| sed 's/\(..\)/0x\1 /g' \
| xargs printf "%d "
echo "83c110200748f37e98f0ae36802eebcc605f023a1661f3731c3620b470c78325" \
| sed 's/\(..\)/0x\1 /g' \
| xargs printf "%d "
When converted to a byte array, the intent ID is represented as:
[ 131, 193, 16, 32, 7, 72, 243, 126, 152, 240, 174, 54, 128, 46, 235, 204,
96, 95, 2, 58, 22, 97, 243, 115, 28, 54, 32, 180, 112, 199, 131, 37 ]
[ 131, 193, 16, 32, 7, 72, 243, 126, 152, 240, 174, 54, 128, 46, 235, 204,
96, 95, 2, 58, 22, 97, 243, 115, 28, 54, 32, 180, 112, 199, 131, 37 ]
Step 2: Encode the GetStatusRequest Message
When encoded, this produces the following hex string:
Details
Using the byte array from Step 1, construct the JSON for the GetStatusRequest
message:
{
"intent_id": {
"value": [
131, 193, 16, 32, 7, 72, 243, 126, 152, 240, 174, 54, 128, 46, 235, 204,
96, 95, 2, 58, 22, 97, 243, 115, 28, 54, 32, 180, 112, 199, 131, 37
]
}
}
{
"intent_id": {
"value": [
131, 193, 16, 32, 7, 72, 243, 126, 152, 240, 174, 54, 128, 46, 235, 204,
96, 95, 2, 58, 22, 97, 243, 115, 28, 54, 32, 180, 112, 199, 131, 37
]
}
}
0a220a2083c110200748f37e98f0ae36802eebcc605f023a1661f3731c3620b470c78325
0a220a2083c110200748f37e98f0ae36802eebcc605f023a1661f3731c3620b470c78325
Step 3: Create the Request Envelope
Wrap the GetStatusRequest
binary payload in a higher-level Request envelope.
Use the following values:
- version: (empty in this example, this field is optional)
- service: "MicroPayment"
- method: "getStatus" (note the camelCase)
- body: The byte array from the previous step
This envelope is encoded into the following final hex string:
0a00120c4d6963726f5061796d656e741a0967657453746174757322240a220a2083c110200748f37e98f0ae36802eebcc605f023a1661f3731c3620b470c78325
0a00120c4d6963726f5061796d656e741a0967657453746174757322240a220a2083c110200748f37e98f0ae36802eebcc605f023a1661f3731c3620b470c78325
Step 4: Save the Binary Payload to a File
Save the final hex string to a file called request.bin.
Details
You can do this using the following command:
echo -n "0a00120c4d6963726f5061796d656e741a0967657453746174757322240a220a2083c110200748f37e98f0ae36802eebcc605f023a1661f3731c3620b470c78325" \
| xxd -r -p > request.bin
echo -n "0a00120c4d6963726f5061796d656e741a0967657453746174757322240a220a2083c110200748f37e98f0ae36802eebcc605f023a1661f3731c3620b470c78325" \
| xxd -r -p > request.bin
Explanation:
echo -n "..."
outputs the hex string without a newline.xxd -r -p
converts the hex string back into binary.
The result is saved in request.bin
.
Step 5: Send the Request Using cURL
Now, send the request to the Code API with cURL:
curl -X POST "https://cash.getcode.com/v1/api" \
-H "Content-Type: application/octet-stream" \
--data-binary @request.bin | xxd
curl -X POST "https://cash.getcode.com/v1/api" \
-H "Content-Type: application/octet-stream" \
--data-binary @request.bin | xxd
Details
Explanation:
-X POST
specifies a POST request.-H "Content-Type: application/octet-stream"
sets the content type to binary.--data-binary @request.bin
sends the binary data from request.bin.
The response is piped to xxd
to display it in hex format.
Step 6: Interpret the Response
Use the protobuf definition for GetStatusResponse
to interpret the response.
Details
A successful response should look like this:
{
"result": "OK",
"body": [ 8, 1, 16, 1, 24, 1 ], // hex: 080110011801
"message": ""
}
{
"result": "OK",
"body": [ 8, 1, 16, 1, 24, 1 ], // hex: 080110011801
"message": ""
}
When decoded using the GetStatusResponse protobuf definition, it represents:
{
"exists": true,
"code_scanned": true,
"intent_submitted": true
}
{
"exists": true,
"code_scanned": true,
"intent_submitted": true
}
- The result indicates overall success
(OK)
. - The body contains the binary-encoded response, which, when decoded, confirms that the intent
exists
, has beenscanned
, andsubmitted
. - The message is empty, meaning no error details were provided.
Payment Example
A typical server-side integration will look something like this:
sequenceDiagram
autonumber
participant User
participant Browser
User->>Browser: Lands on payment page
Browser->>Server: Sends order information
Server->>Code Sequencer: POST /v1/createIntent
Code Sequencer->>Server: Returns PaymentIntent status
Server->>Browser: Returns PaymentIntent's client_secret
Browser->>Code Sequencer: Opens message stream at rendezvous value
sequenceDiagram
autonumber
participant User
participant Browser
User->>Browser: Lands on payment page
Browser->>Server: Sends order information
Server->>Code Sequencer: POST /v1/createIntent
Code Sequencer->>Server: Returns PaymentIntent status
Server->>Browser: Returns PaymentIntent's client_secret
Browser->>Code Sequencer: Opens message stream at rendezvous value
You can learn more about the payment flow here.
In order to call the createIntent
API manually, you'll need to construct the payment Intent binary blob yourself and sign it with the rendezvous key.
You can do that one of two ways:
- Use a protobuf library to create the binary payload
- Construct the binary payload manually
The protobuf defenition for RequestToReceiveBill
is as follows (full definition can be found here):
Details
syntax = "proto3";
message SolanaAccountId {
bytes value = 1;
}
message Signature {
bytes value = 1;
}
message MessageId {
bytes value = 1;
}
message Message {
MessageId id = 1;
Signature send_message_request_signature = 3;
oneof kind {
RequestToReceiveBill request_to_receive_bill = 5;
}
}
message RequestToReceiveBill {
SolanaAccountId requestor_account = 1;
oneof exchange_data {
ExchangeData exact = 2;
ExchangeDataWithoutRate partial = 3;
}
}
message ExchangeData {
string currency = 1;
double exchange_rate = 2;
double native_amount = 3;
uint64 quarks = 4;
}
message ExchangeDataWithoutRate {
string currency = 1;
double native_amount = 2;
}
syntax = "proto3";
message SolanaAccountId {
bytes value = 1;
}
message Signature {
bytes value = 1;
}
message MessageId {
bytes value = 1;
}
message Message {
MessageId id = 1;
Signature send_message_request_signature = 3;
oneof kind {
RequestToReceiveBill request_to_receive_bill = 5;
}
}
message RequestToReceiveBill {
SolanaAccountId requestor_account = 1;
oneof exchange_data {
ExchangeData exact = 2;
ExchangeDataWithoutRate partial = 3;
}
}
message ExchangeData {
string currency = 1;
double exchange_rate = 2;
double native_amount = 3;
uint64 quarks = 4;
}
message ExchangeDataWithoutRate {
string currency = 1;
double native_amount = 2;
}
Binary Message
Given the protobuf definition, you can construct a RequestToReceiveBill
as follows:
[
0x2A, 0x34, 0x0A, 0x22, 0x0A, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x1A, 0x0E, 0x0A, 0x03, 0x75, 0x73, 0x64, 0x11, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x24, 0x40
]
[
0x2A, 0x34, 0x0A, 0x22, 0x0A, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x1A, 0x0E, 0x0A, 0x03, 0x75, 0x73, 0x64, 0x11, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x24, 0x40
]
For the following data:
{
"requestor_account": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=",
"partial": {
"currency": "usd",
"native_amount": 10.0
}
}
{
"requestor_account": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=",
"partial": {
"currency": "usd",
"native_amount": 10.0
}
}
Details
Let's decode the given binary serialization based on the protobuf definitions provided earlier.
Message structure
0x2A
: This indicates field number 5 (request_to_receive_bill
) with a wire type of 2 (length-delimited).0x34
: The length of the upcomingRequestToReceiveBill
message is 52 bytes.
RequestToReceiveBill structure
0x0A
: This indicates field number 1 (requestor_account
) with a wire type of 2 (length-delimited).0x22
: The length of the upcomingSolanaAccountId
message is 34 bytes.
SolanaAccountId structure
0x0A
: This indicates field number 1 (value) with a wire type of 2 (length-delimited).0x20
: The length of the upcoming value is 32 bytes.- Following 32 bytes: 0x00 x 32: This is the account ID in bytes format, which in our example, is all zeros.
RequestToReceiveBill structure (continued)
0x1A
: This indicates field number 3 (partial, which is of typeExchangeDataWithoutRate
) with a wire type of 2 (length-delimited).0x0E
: The length of the upcomingExchangeDataWithoutRate
message is 14 bytes.
ExchangeDataWithoutRate structure:
0x0A
: This indicates field number 1 (currency) with a wire type of 2 (length-delimited).0x03
: The length of the currency string is 3 bytes.- Following 3 bytes:
0x75 0x73 0x64
: This is ASCII for the string"usd"
. 0x11
: This indicates field number 2 (native_amount) with a wire type of 1 (64-bit).- Following 8 bytes:
0x00 0x00 0x00 0x00 0x00 0x00 0x24 0x40
: This is the number representation of10.0
for the native amount.
Decoded values
From this binary serialization, the values decoded are:
{
"requestor_account": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=",
"partial": {
"currency": "usd",
"native_amount": 10.0
}
}
{
"requestor_account": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=",
"partial": {
"currency": "usd",
"native_amount": 10.0
}
}
This is then signed using the rendezvous key and POST
ed to the Code Sequencer's createIntent
endpoint.
You can see an example of this in our Python SDK.
Login Example
The Login with Code flow consists of the following steps, you can learn more about the login flow here.
sequenceDiagram
autonumber
participant User
participant Browser
User->>Browser: Lands on login page
Browser->>Server: Sends login request
Server->>Code Sequencer: POST /v1/createIntent
Code Sequencer->>Server: Returns LoginIntent status
Server->>Browser: Returns LoginIntent's client_secret
Browser->>Code Sequencer: Open message stream at rendezvous value
sequenceDiagram
autonumber
participant User
participant Browser
User->>Browser: Lands on login page
Browser->>Server: Sends login request
Server->>Code Sequencer: POST /v1/createIntent
Code Sequencer->>Server: Returns LoginIntent status
Server->>Browser: Returns LoginIntent's client_secret
Browser->>Code Sequencer: Open message stream at rendezvous value
The login flow is similar to the payment flow, but instead of a payment Intent, you'll be creating a login Intent. The login Intent is a higher-level abstraction that represents a user's Intent to log in to a website or application.
Details
The protobuf definition for RequestToLogin is as follows:
syntax = "proto3";
message Domain {
string domain = 1;
}
message SolanaAccountId {
bytes value = 1;
}
message Signature {
bytes value = 1;
}
message RendezvousKey {
bytes value = 1;
}
message MessageId {
bytes value = 1;
}
message Message {
MessageId id = 1;
Signature send_message_request_signature = 3;
oneof kind {
RequestToLogin request_to_login = 10;
}
}
message RequestToLogin {
Domain domain = 1;
SolanaAccountId verifier = 4;
Signature signature = 5;
RendezvousKey rendezvous_key = 6;
}
syntax = "proto3";
message Domain {
string domain = 1;
}
message SolanaAccountId {
bytes value = 1;
}
message Signature {
bytes value = 1;
}
message RendezvousKey {
bytes value = 1;
}
message MessageId {
bytes value = 1;
}
message Message {
MessageId id = 1;
Signature send_message_request_signature = 3;
oneof kind {
RequestToLogin request_to_login = 10;
}
}
message RequestToLogin {
Domain domain = 1;
SolanaAccountId verifier = 4;
Signature signature = 5;
RendezvousKey rendezvous_key = 6;
}
You can construct a login Intent using similar logic as the payment Intent, but with the RequestToLogin protobuf definition.
INFO
Use the TypeScript version as a reference for constructing the binary blob.
Contributing
If you're interested in contributing to the Code SDK, or one of our variants, please reach out to us on Discord or Twitter. We're always looking for help with new SDK features, documentation, and examples.
If there is a feature that you need that isn't supported by the SDKs yet, it is likely that someone else needs it too. We can work together to add the feature to the SDKs.