Skip to content

Builder Methods

Builder methods extend the standard L2 client with extra HMAC-signed headers identifying your builder account. Every order and trade routed through builder endpoints carries your builder_id for volume attribution.

Builder methods require promoting an already-authenticated client with builder credentials.

Use when your backend controls both user and builder keys:

use openfish_client_sdk::auth::{Credentials, builder::Config as BuilderConfig};
use openfish_client_sdk::clob::{Client, Config};
// First, authenticate as a normal user
let client = Client::new("https://api.openfish.fun", Config::default())?
.authentication_builder(&signer)
.authenticate()
.await?;
// Then promote to builder with local credentials
let builder_creds = Credentials::new(
std::env::var("OPENFISH_BUILDER_API_KEY")?.parse()?,
std::env::var("OPENFISH_BUILDER_SECRET")?,
std::env::var("OPENFISH_BUILDER_PASSPHRASE")?,
);
let builder_config = BuilderConfig::local(builder_creds);
let builder_client = client.promote_to_builder(builder_config).await?;

Use when builder keys live on a separate signing server:

let builder_config = BuilderConfig::remote(
"https://your-server.com/sign",
Some("optional-auth-token".to_owned()),
)?;
let builder_client = client.promote_to_builder(builder_config).await?;

The remote server receives { method, path, body, timestamp } and returns:

{
"OPENFISH_BUILDER_API_KEY": "...",
"OPENFISH_BUILDER_TIMESTAMP": "...",
"OPENFISH_BUILDER_PASSPHRASE": "...",
"OPENFISH_BUILDER_SIGNATURE": "..."
}

Never expose builder credentials in client-side code. Use environment variables or a secrets manager.


The SDK automatically includes these headers on builder requests:

HeaderDescription
OPENFISH_BUILDER_API_KEYBuilder API key
OPENFISH_BUILDER_TIMESTAMPUnix timestamp of signature
OPENFISH_BUILDER_PASSPHRASEBuilder passphrase
OPENFISH_BUILDER_SIGNATUREHMAC-SHA256 signature

With local signing, the SDK computes these from the credentials. With remote signing, the signing server provides them.


Post an order with builder attribution. The request body is the same as POST /order; builder headers are attached automatically.

use openfish_client_sdk::clob::types::Side;
use openfish_client_sdk::types::dec;
let order = builder_client
.limit_order()
.token_id("TOKEN_ID".parse()?)
.price(dec!(0.50))
.size(dec!(10))
.side(Side::Buy)
.build()
.await?;
let signed = builder_client.sign(&signer, order).await?;
let response = builder_client.post_order(signed).await?;
println!("Order: {} ({})", response.order_id, response.status);

The matching engine treats builder orders identically to standard orders. The only difference is the builder_id recorded in the orders and trades tables.


Cancel an order with builder attribution.

let resp = builder_client.cancel_order("ORDER_ID").await?;
println!("Canceled: {:?}", resp.canceled);

REST:

Terminal window
POST /builder/cancel
OPENFISH_BUILDER_ID: my-builder-platform
Content-Type: application/json
{"orderID": "a1b2c3d4-..."}

Retrieve trades linked to your builder account.

use openfish_client_sdk::clob::types::request::TradesRequest;
// All builder trades
let trades = builder_client.builder_trades(&TradesRequest::default(), None).await?;
for trade in &trades.data {
println!("{}: {} {} at {} (status: {})",
trade.id, trade.side, trade.size, trade.price, trade.status);
}
// Filtered by market
let request = TradesRequest::builder()
.market("CONDITION_ID".parse()?)
.build();
let market_trades = builder_client.builder_trades(&request, None).await?;

BuilderTrade fields:

FieldTypeDescription
idstringTrade ID
builderstringBuilder address
marketstringCondition ID
assetIdstringToken ID
sidestringBUY or SELL
sizestringTrade size in shares
sizeUsdcstringTrade size in USDC
pricestringExecution price
statusstringTrade status
ownerstringTaker address
makerstringMaker address
transactionHashstringOnchain tx hash
matchTimestringWhen the trade matched
feestringFee in shares
feeUsdcstringFee in USDC

When a BuilderConfig is present, order and open-order queries use builder authentication headers. This returns orders attributed to the builder rather than the user’s own orders.

// Get open orders placed through this builder
let orders = builder_client.orders(&OrdersRequest::default(), None).await?;
// Get a specific order
let order = builder_client.order("ORDER_ID").await?;

If your builder credentials are compromised, revoke them right away:

builder_client.revoke_builder_api_key().await?;

After revocation, generate new credentials and update your builder configuration. The revoked key can no longer authenticate builder requests.


Create a builder API key:

let builder_key = builder_client.create_builder_api_key().await?;
println!("Builder API key: {:?}", builder_key);

The builder endpoint was called without the required header. Make sure your client has been promoted with promote_to_builder().

  • Confirm the request body matches what the signing server signed
  • Check that method, path, and body are consistent between client and server
  • Verify builder credentials have not been revoked
  • Confirm your builder credentials are valid
  • Make sure orders go through builder endpoints, not standard endpoints
  • Check that trades have status CONFIRMED

  • L2 Methods — Standard authenticated methods -> l2
  • Order Attribution — How builder_id flows through the system -> ../orders/attribution
  • Gasless Transactions — Relay transactions without gas -> ../gasless