Yokefellow - Builder Docs
SDK Guide
The packaged client guide for Yokefellow reads, writes, auth, wallet context, signing boundaries, and lifecycle flows.

1. What the SDK is
The Yokefellow SDK is the packaged client surface for working with the platform’s read and write flows from an app or partner integration. In the current repo, that package is @yokefellow/sdk-client, and its entry point is createYfSdkClient(...). The SDK wraps the main HTTP surface behind one client so builders do not have to hand-build every request, auth header, query string, and error check themselves.
At the time of writing, the SDK is centered on the actual Yokefellow primitives and flows that already exist in the product surface. That includes bucket reads, funding flows, offering request flows, direct buy and craft flows, earned event submission, queue reads and queue processing writes, market reads, activity reads, transaction status checks, and transaction finalization. In other words, the SDK is not a vague helper library. It is the packaged way to work with the real Yokefellow integration surface.
The current client is created with a small option set:
baseUrl
appKey
bearerToken
fetch
defaultHeaders
That shape matters because it tells you what the SDK is responsible for. It is responsible for request construction, auth header handling, JSON request and response handling, and consistent error throwing when the returned payload or HTTP response is not successful. It is not trying to hide Yokefellow’s lifecycle model. It is trying to make that model easier to work with from code.
In practice, the SDK is the best starting point when you want to build against Yokefellow through the intended packaged surface instead of wiring raw requests from scratch. If you need direct endpoint lookup and lower-level control, the API Reference is the better companion. But for most first integrations, the SDK is the fastest path from setup to working reads and writes.
2. Install and setup
The current SDK package in the repo is:
@yokefellow/sdk-client
Inside the current controlled-access builder model, the normal starting point is to add that package to the app or service that will integrate with Yokefellow, then create one client instance that points at the correct SDK base URL.
A minimal client setup looks like this:
import { createYfSdkClient } from "@yokefellow/sdk-client";
const sdk = createYfSdkClient({ baseUrl: "https://yokefellow.io/api/sdk/v1", appKey: process.env.YF_APP_KEY, });
That is the real client creation pattern in the package today. baseUrl is required. The other fields are optional and depend on how your integration is authenticated.
2.1 Required base configuration
Every SDK client needs a baseUrl. This should point at the Yokefellow SDK route family rather than at the top of the site.
Example:
const sdk = createYfSdkClient({ baseUrl: "https://yokefellow.io/api/sdk/v1", });
The client trims trailing slashes automatically, so builders do not need to normalize the base URL themselves before creating the client.
2.2 Auth configuration
The current SDK supports two main auth inputs:
appKey
bearerToken
If a bearer token is present, the client uses:
authorization: Bearer <token>
If no bearer token is present but an app key is present, the client uses:
x-yf-app-key: <appKey>
That means the normal controlled-access setup today is usually app-key based unless your integration has a bearer-token flow available.
Example with app key:
const sdk = createYfSdkClient({ baseUrl: "https://yokefellow.io/api/sdk/v1", appKey: process.env.YF_APP_KEY, });
Example with bearer token:
const sdk = createYfSdkClient({ baseUrl: "https://yokefellow.io/api/sdk/v1", bearerToken: process.env.YF_BEARER_TOKEN, });
2.3 Optional fetch and headers
The SDK also accepts:
fetch
defaultHeaders
That is useful when the integration is running outside the default runtime fetch environment, or when a team wants to add shared headers across requests.
Example:
const sdk = createYfSdkClient({ baseUrl: "https://yokefellow.io/api/sdk/v1", appKey: process.env.YF_APP_KEY, defaultHeaders: { "x-app-name": "snake-test", }, });
Advanced note:
The package also exports request(...), buildSdkAuthHeaders(...), and the SDK types. Most builders should start with the named convenience methods in this guide, but advanced integrations can use request(...) as an escape hatch for route-level access, buildSdkAuthHeaders(...) when they need to compose auth headers outside the client, and the exported types to keep request and response handling aligned with the package.
import { buildSdkAuthHeaders, createYfSdkClient, type DepositRequest, } from "@yokefellow/sdk-client";
2.4 What setup should prove first
Before building larger flows, setup should prove one thing: the client can talk to a real Yokefellow surface successfully.
A good first setup test is a simple read such as:
getBucketCatalog(bucketId)
getMarketOrderbook()
getAppSession()
Example:
const session = await sdk.getAppSession(); console.log(session);
Or, for a public read:
const orderbook = await sdk.getMarketOrderbook(); console.log(orderbook);
The goal at setup time is not to wire the whole app. The goal is to prove:
the base URL is correct
auth is correct for the path being used
the client can make a successful request
returned JSON is being handled correctly
3. Auth and signing model
The current SDK has three separate layers that matter to a builder:
request auth
wallet context
chain-backed signing
They are related, but they are not the same thing.
3.1 Request auth
At the client level, the SDK supports two auth inputs:
appKey
bearerToken
The current auth order is:
if bearerToken is present, the client sends authorization: Bearer <token>
otherwise, if appKey is present, the client sends x-yf-app-key: <appKey>
otherwise, no auth header is attached
A normal client looks like this:
import { createYfSdkClient } from "@yokefellow/sdk-client";
const sdk = createYfSdkClient({ baseUrl: "https://your-domain.com/api/sdk/v1", appKey: process.env.YF_APP_KEY, });
If your integration uses bearer auth instead:
const sdk = createYfSdkClient({ baseUrl: "https://your-domain.com/api/sdk/v1", bearerToken: process.env.YF_BEARER_TOKEN, });
3.2 Public reads versus app-auth reads
The SDK does not treat every route the same way.
Some reads are intentionally public and do not require app auth. In the current client surface, examples include:
getBucketCatalog(...)
getWalletEntitlements(...)
getBucketFunding(...)
getMarketOrderbook(...)
getMarketTrades(...)
getYesPriceStats(...)
getActivityFeed(...)
That means you can start with real reads before proving an authenticated app path.
By contrast, app-scoped and strictly gated flows such as these normally run through the authenticated request path:
getAppSession()
createBucketOfferingRequest(...)
buyBucketOffering(...)
craftBucketOffering(...)
submitBucketOfferingEvent(...)
queue reads and queue actions
Funding and transaction lifecycle helpers sit slightly differently in the current surface. createDeposit(...), createWithdraw(...), createTransferBetweenBuckets(...), getTransactionStatus(...), and finalizeTransaction(...) are lifecycle helpers. Your SDK client will send app auth when configured, but the underlying route behavior is not the same hard gate as app session or queue/configuration routes. So use getAppSession() to prove authenticated integration recognition rather than assuming a successful lifecycle helper call proves the same thing.
A simple auth check is:
const session = await sdk.getAppSession(); console.log(session);
If that fails, fix auth before testing write flows.
3.3 Wallet context
Wallet context is not the same thing as request auth.
Request auth proves the app or integration can talk to Yokefellow. Wallet context tells Yokefellow which wallet the current read or action is about.
You can see that directly in the current client surface. For example:
const catalog = await sdk.getBucketCatalog(bucketId, { wallet: "0xYourWalletAddress", });
And for an offering request:
const result = await sdk.createBucketOfferingRequest(bucketId, { wallet: "0xYourWalletAddress", offeringId: "your-offering-id", selectedOutputId: null, });
And for a deposit:
const prepared = await sdk.createDeposit({ bucketId, wallet: "0xYourWalletAddress", amountYesRaw: "1000000000000000000", });
So the rule is:
auth identifies the integration
wallet identifies the user-side context of the read or action
3.4 Signing boundary
The SDK is not the wallet signer.
For chain-backed flows, the SDK helps your app prepare and track the Yokefellow-facing lifecycle around the action, but the actual wallet signature or transaction submission still belongs to the wallet or chain layer your app uses.
That is easiest to understand through deposit flow:
use the SDK to prepare the deposit
submit the chain-backed transaction through the wallet path
use the SDK to check transaction status
finalize if the flow requires it
read back the resulting state
The current lifecycle helpers for that are:
createDeposit(...)
getTransactionStatus(...)
finalizeTransaction(...)
Example status check:
const status = await sdk.getTransactionStatus({ txHash: "0xYourTransactionHash", kind: "deposit", bucketId, });
Example finalize call:
const finalized = await sdk.finalizeTransaction({ kind: "deposit", txHash: "0xYourTransactionHash", bucketId, wallet: "0xYourWalletAddress", });
That is the practical signing boundary:
the wallet signs the chain action
the SDK tracks and finalizes the Yokefellow-side lifecycle around it
3.5 What to remember
If you only remember four things, remember these:
appKey and bearerToken are request-auth inputs
some reads are public and do not require app auth
wallet context is passed per read or write where relevant
the SDK does not replace wallet signing; it helps you handle the platform lifecycle around it
4. Read flows
The SDK read surface is easiest to understand when grouped by what the builder is trying to answer. In the current client, the main read groups are:
bucket-facing reads
wallet-facing reads
funding and request-state reads
queue reads
market reads
activity reads
app-session reads
That is a more useful way to learn the SDK than reading methods in isolation.
4.1 Bucket-facing reads
Use these when your app is centered on a bucket as the main participation surface.
getBucketCatalog(bucketId, query?)
This is the most useful general bucket read. It is usually the best first real object to fetch because it gives your app a live participation surface to anchor to.
import { createYfSdkClient } from "@yokefellow/sdk-client";
const sdk = createYfSdkClient({ baseUrl: "https://yokefellow.io/api/sdk/v1", appKey: process.env.YF_APP_KEY, });
const bucketId = "your-bucket-id";
const catalog = await sdk.getBucketCatalog(bucketId); console.log(catalog);
If your integration needs wallet-aware state for the same bucket, pass the wallet in the query:
const catalog = await sdk.getBucketCatalog(bucketId, { wallet: "0xYourWalletAddress", });
The current query shape supports:
wallet
round
getBucketFunding(bucketId, query?)
Use this when your integration cares about funding state rather than the broader catalog shape.
const funding = await sdk.getBucketFunding(bucketId, { wallet: "0xYourWalletAddress", });
console.log(funding);
The current query shape supports:
wallet
round
4.2 Wallet-facing reads
Use these when your app is centered on what one wallet currently has or can do.
These methods read state for the wallet address you pass in. They do not assume the SDK caller is reading its own wallet.
getWalletEntitlements(wallet, query?)
This is the current SDK read for wallet-linked rights or output state.
const entitlements = await sdk.getWalletEntitlements( "0xYourWalletAddress", { bucketId: "your-bucket-id", includePending: true, limit: 20, } );
console.log(entitlements);
The current query shape supports:
bucketId
includePending
round
limit
This is the right read when your app needs to answer questions like:
what does this wallet currently hold
what rights or outputs are attached to this wallet
what is pending versus already resolved
4.3 Request and lifecycle reads
Use these when your app needs to confirm the state of participation paths after a write.
getBucketOfferingRequests(bucketId, query?)
This is the main read for request-based participation state.
const requests = await sdk.getBucketOfferingRequests("your-bucket-id", { wallet: "0xYourWalletAddress", limit: 10, });
console.log(requests);
The current query shape supports:
wallet
id
status
limit
This is the read you use after createBucketOfferingRequest(...) when you want to confirm the result through returned state instead of guessing.
getTransactionStatus(query)
This is the main read for tracked transaction state.
const status = await sdk.getTransactionStatus({ txHash: "0xYourTransactionHash", kind: "deposit", bucketId: "your-bucket-id", });
console.log(status);
This is the important lifecycle read after a deposit, withdraw, transfer, or other tracked action where the app needs to know what Yokefellow currently thinks happened.
4.4 Queue reads
Use these when your integration cares about work that is pending, reviewable, or operator-processed.
getMintQueue(query)
const mintQueue = await sdk.getMintQueue({ bucketId: "your-bucket-id", status: "pending", limit: 20, });
console.log(mintQueue);
The current query shape supports:
bucketId
status
limit
getRequestQueue(query)
const requestQueue = await sdk.getRequestQueue({ bucketId: "your-bucket-id", wallet: "0xYourWalletAddress", status: "pending", limit: 20, });
console.log(requestQueue);
The current query shape supports:
bucketId
wallet
id
status
limit
These queue reads matter when your app is helping operators or advanced participants inspect work that has not fully resolved yet.
When your integration needs to move queue work forward rather than only inspect it, the paired write is runMintQueue(...). That method belongs in the write surface because it processes queued mint work instead of only reading queue state.
4.5 Market reads
Use these when your app needs YES market state rather than bucket-specific state.
getMarketOrderbook(query?)
const orderbook = await sdk.getMarketOrderbook(); console.log(orderbook);
getMarketTrades(query?)
const trades = await sdk.getMarketTrades({ limit: 25 }); console.log(trades);
getYesPriceStats(query)
const stats = await sdk.getYesPriceStats({ window: "24h", });
console.log(stats);
These are the current read methods for:
visible orderbook state
recent trade flow
YES pricing stats
They are useful for apps that need market context, price discovery, or recent movement without going through bucket reads first.
4.6 Activity reads
Use this when your app needs recent visible system activity.
getActivityFeed(query?)
const activity = await sdk.getActivityFeed({ limit: 25, });
console.log(activity);
This is the read for the recent activity surface exposed through the SDK.
4.7 App-scoped read
Use this when your first question is whether the current app client is recognized correctly.
getAppSession()
const session = await sdk.getAppSession(); console.log(session);
This is the cleanest first authenticated read because it proves the client can make a real app-scoped request before you move into writes.
4.8 Practical read sequences
The right read sequence depends on the kind of app you are building.
If your app is bucket-centered, start with:
getBucketCatalog(...)
getBucketFunding(...)
getBucketOfferingRequests(...) after a request flow
If your app is wallet-centered, start with:
getWalletEntitlements(...)
getBucketCatalog(...) with wallet context
getTransactionStatus(...) after a tracked action
If your app is operator-centered, start with:
getRequestQueue(...)
getMintQueue(...)
getActivityFeed(...)
If your app is market-centered, start with:
getMarketOrderbook(...)
getMarketTrades(...)
getYesPriceStats(...)
That is a more useful way to learn the read surface than memorizing method names one by one.
5. Write flows
The current SDK write surface is easiest to learn by grouping actions by what they change. In the current client, the most important write groups are:
funding flows
offering participation flows
earned event and queue-processing writes
lifecycle finalization flows
That is a better way to approach the SDK than treating every write as one unrelated method.
5.1 Funding flows
Use these when your app is moving value into, out of, or between Yokefellow surfaces.
createDeposit(input)
Use this when your app needs to prepare a bucket deposit flow.
const prepared = await sdk.createDeposit({ bucketId: "your-bucket-id", wallet: "0xYourWalletAddress", amountYesRaw: "1000000000000000000", });
console.log(prepared);
This is the normal starting point for a chain-backed funding flow. It prepares the Yokefellow-side deposit action, but it does not replace the wallet-side transaction submission. That still belongs to the wallet or chain layer your app uses.
createWithdraw(input)
Use this when your app needs to prepare a withdraw flow from a supported bucket balance back to the wallet side.
const prepared = await sdk.createWithdraw({ bucketId: "your-bucket-id", wallet: "0xYourWalletAddress", amountYesRaw: "500000000000000000", });
console.log(prepared);
Withdraw begins with the same SDK preparation idea as deposit, but the current repo flow diverges after that. The prepare response can return a signature payload your app must sign and resubmit through createWithdraw(...). Track the resulting state through getTransactionStatus(...). In the current SDK types, withdraw does not use finalizeTransaction(...).
createTransferBetweenBuckets(input)
Use this when your app needs to move value from one bucket context to another.
const prepared = await sdk.createTransferBetweenBuckets({ fromBucketId: "source-bucket-id", toBucketId: "target-bucket-id", ownerWallet: "0xYourWalletAddress", amountYesRaw: "1000000000000000000", });
console.log(prepared);
This is the funding write for bucket-to-bucket movement rather than wallet-to-bucket or bucket-to-wallet movement.
5.2 Offering participation flows
Use these when your app is creating participation state around offerings rather than moving funds directly.
createBucketOfferingRequest(bucketId, input)
Use this when the participation path is request-based.
const result = await sdk.createBucketOfferingRequest("your-bucket-id", { wallet: "0xYourWalletAddress", offeringId: "your-offering-id", selectedOutputId: null, });
console.log(result);
This is the cleanest first app-level write because it creates real state change without forcing you to begin with a funding transaction. It is usually the best first write when you want to prove that your integration can create participation state and confirm it afterward through request history.
buyBucketOffering(bucketId, input)
Use this when the offering path is a direct buy flow.
const result = await sdk.buyBucketOffering("your-bucket-id", { wallet: "0xYourWalletAddress", offeringId: "your-offering-id", qty: 1, selectedOutputId: null, });
console.log(result);
This is the write path for direct offering purchase rather than request-based participation. Your app should treat it as a real participation write, not as a generic storefront action. The result still needs to be confirmed through the returned state and any related lifecycle reads.
craftBucketOffering(bucketId, input)
Use this when the participation path is a craft-style flow rather than a normal buy or request.
const result = await sdk.craftBucketOffering("your-bucket-id", { wallet: "0xYourWalletAddress", offeringId: "your-offering-id", selectedOutputId: null, });
console.log(result);
This is the write path for craft-based participation where the offering itself is doing more than simple purchase. In repo-real flows, craft often depends on configured input rules and wallet-held prerequisites rather than behaving like a normal buy with different copy. Your app should therefore confirm the resulting state through the relevant entitlement, request, or queue surface instead of assuming craft is always instant.
5.3 Earned event and queue-processing writes
Use these when your app needs to submit earned-path signals or move queued mint work forward.
submitBucketOfferingEvent(bucketId, input)
Use this when your app needs to submit an app-side event, score, or completion signal into an earned offering path.
const result = await sdk.submitBucketOfferingEvent("your-bucket-id", { wallet: "0xYourWalletAddress", appSlug: "snake-test", eventType: "run_completed", metrics: { score: 1200 }, });
console.log(result);
This method matters because some app outcomes are earned rather than bought or requested directly. It lets the app submit the event that the offering logic resolves against instead of inventing a separate reward backend.
runMintQueue(input)
Use this when your integration needs to process queued mint work rather than only inspect the queue.
const result = await sdk.runMintQueue({ bucketId: "your-bucket-id", limit: 10, retryFailed: false, });
console.log(result);
This is the write path for queue processing. It belongs beside getMintQueue(...) because operator-aware or advanced integrations often need both: one method to inspect pending mint work and one method to advance it.
5.4 Lifecycle finalization flow
Use this when a chain-backed or tracked action has already happened and your app needs to complete the Yokefellow-side lifecycle.
finalizeTransaction(input)
const finalized = await sdk.finalizeTransaction({ kind: "deposit", txHash: "0xYourTransactionHash", bucketId: "your-bucket-id", wallet: "0xYourWalletAddress", });
console.log(finalized);
This method matters because some flows are not complete just because the transaction exists. A tracked action may still need Yokefellow-side finalization before your app should treat it as resolved. In the current SDK types, finalizeTransaction(...) is used for deposit and transfer-between-buckets, not withdraw.
5.5 Practical write sequences
The right write sequence depends on the kind of participation or value movement your app is built around.
If your app is centered on request-based participation, the normal sequence is:
createBucketOfferingRequest(...)
getBucketOfferingRequests(...) to read the resulting request state
If your app is centered on direct offering participation, the normal sequence is:
buyBucketOffering(...)
the related bucket, entitlement, or lifecycle read that confirms the resulting state
If your app is centered on funding flows, the normal sequence is:
createDeposit(...)
wallet-side transaction submission
getTransactionStatus(...)
finalizeTransaction(...) where the lifecycle requires it
If your app is centered on earned event resolution, the normal sequence is:
submitBucketOfferingEvent(...)
the related entitlement, request, or queue read that confirms what the event resolved into
If your app is centered on queued mint work, the normal sequence is:
getMintQueue(...)
runMintQueue(...)
the related queue, entitlement, or activity read that confirms the outstanding work moved
If your app is centered on bucket-to-bucket value movement, the normal sequence is:
createTransferBetweenBuckets(...)
getTransactionStatus(...)
the related funding read that confirms the resulting state
What matters is not the write call alone. Each write belongs to a larger lifecycle. A builder should choose the write flow that matches the app’s role, then pair it with the read surface that confirms what the action became inside Yokefellow.
5.6 What to remember
Use createBucketOfferingRequest(...) for request-based participation. Use buyBucketOffering(...) for direct offering participation. Use createDeposit(...) for funding flows. Use createTransferBetweenBuckets(...) for bucket-to-bucket value movement. Use finalizeTransaction(...) where the lifecycle requires explicit Yokefellow-side completion.
6. Transaction lifecycle
The SDK does not treat every transaction-like flow as one generic “submit and wait” pattern. In the current surface, lifecycle handling depends on the kind of action you are performing. That is why the SDK returns structured lifecycle descriptors instead of only a success flag. A builder is expected to read those descriptors and follow the flow the response actually describes.
At the practical level, the main funding lifecycle types in the current SDK are:
deposit
withdraw
transfer between buckets
They do not all move through the same phases. A deposit flow is not shaped the same way as a withdraw flow, and a transfer flow is not shaped the same way as either of those. That difference belongs in the SDK Guide because it affects how your app should react to the response it receives.
6.1 Deposit lifecycle
A deposit flow starts with createDeposit(...).
const prepared = await sdk.createDeposit({ bucketId: "your-bucket-id", wallet: "0xYourWalletAddress", amountYesRaw: "1000000000000000000", });
console.log(prepared);
In the current SDK surface, a deposit response can describe a prepare phase or a capture phase. In practice, the first response usually gives your app the information it needs to move into the chain-backed part of the flow. That can include:
whether approval is required
the onchain transaction call shape
the next action descriptor
the status route
whether finalization is required
capture information where relevant
That means your app should not treat the deposit response as “done.” It should treat it as the beginning of a guided lifecycle.
A normal deposit sequence looks like this:
call createDeposit(...)
inspect the returned response
complete any required wallet-side approval or transaction
use getTransactionStatus(...) to read the tracked result
use finalizeTransaction(...) where the lifecycle says finalization is still required
Status check:
const status = await sdk.getTransactionStatus({ txHash: "0xYourTransactionHash", kind: "deposit", bucketId: "your-bucket-id", });
console.log(status);
Finalization where required:
const finalized = await sdk.finalizeTransaction({ kind: "deposit", txHash: "0xYourTransactionHash", bucketId: "your-bucket-id", wallet: "0xYourWalletAddress", });
console.log(finalized);
6.2 Withdraw lifecycle
A withdraw flow starts with createWithdraw(...).
const prepared = await sdk.createWithdraw({ bucketId: "your-bucket-id", wallet: "0xYourWalletAddress", amountYesRaw: "500000000000000000", });
console.log(prepared);
Withdraw is different from deposit because the current SDK surface supports a signature-based submit step. In other words, the response is not only preparing a chain-backed action. It can also return a signature payload your app must use to continue the flow.
That means a normal withdraw sequence is:
call createWithdraw(...)
read the returned signature requirement if present
sign the returned message through the wallet path
resubmit the withdraw flow with issuedAt, message, and signature
track the resulting transaction or returned state through the status surface
That second submit call uses the same method name, but with the signed submit shape:
const submitted = await sdk.createWithdraw({ bucketId: "your-bucket-id", wallet: "0xYourWalletAddress", amountYesRaw: "500000000000000000", issuedAt: "2026-04-09T12:00:00.000Z", message: "signed message returned by the prepare step", signature: "0xYourSignature", });
console.log(submitted);
So the important thing for builders is not only that withdraw exists. It is that withdraw has a different lifecycle shape from deposit. Your app should be prepared to follow the returned signature path rather than assuming every funding action uses the same transaction pattern. In the current SDK types, withdraw is tracked through its own submit and status flow instead of going through finalizeTransaction(...).
6.3 Transfer-between-buckets lifecycle
A transfer flow starts with createTransferBetweenBuckets(...).
const prepared = await sdk.createTransferBetweenBuckets({ fromBucketId: "source-bucket-id", toBucketId: "target-bucket-id", ownerWallet: "0xYourWalletAddress", amountYesRaw: "1000000000000000000", });
console.log(prepared);
Like deposit, a transfer-between-buckets flow can return a lifecycle that includes:
onchain transaction details
next action information
status route information
finalization requirements
capture information where relevant
A normal transfer sequence is:
call createTransferBetweenBuckets(...)
complete the wallet-side or chain-backed transaction path
use getTransactionStatus(...) to read tracked transaction state
use finalizeTransaction(...) where the lifecycle requires it
Status check:
const status = await sdk.getTransactionStatus({ txHash: "0xYourTransactionHash", kind: "transfer-between-buckets", fromBucketId: "source-bucket-id", toBucketId: "target-bucket-id", });
console.log(status);
Finalization where required:
const finalized = await sdk.finalizeTransaction({ kind: "transfer-between-buckets", txHash: "0xYourTransactionHash", fromBucketId: "source-bucket-id", toBucketId: "target-bucket-id", ownerWallet: "0xYourWalletAddress", });
console.log(finalized);
This is the funding lifecycle for value movement between bucket contexts rather than between wallet and bucket.
6.4 Status and finalization
The two core lifecycle helpers are:
getTransactionStatus(...)
finalizeTransaction(...)
getTransactionStatus(...) is the read side of lifecycle tracking. It tells your app what Yokefellow currently thinks happened for the tracked transaction.
finalizeTransaction(...) is the write side of lifecycle completion where the current flow still requires explicit Yokefellow-side finalization.
A builder should read transaction status as part of the normal lifecycle, not as an exceptional recovery step. In the current surface, status is one of the main ways your app turns raw transaction progress into app-visible state.
6.5 What your app should keep from lifecycle responses
Your app should not throw away lifecycle metadata after the first call. In the current SDK surface, the important fields are the ones that let your app continue the flow and confirm the result cleanly. In practice, that usually means keeping:
transaction hash
kind
bucket or wallet identifiers relevant to the flow
whether finalization is required
the returned next-action information
the returned status path or status-ready context
Those are the fields that let your app move from preparation into tracked completion instead of treating the lifecycle like an opaque black box.
6.6 Practical rule
The simplest way to think about the lifecycle surface is this:
prepare the action through the SDK
complete the wallet-side or chain-side step where required
read transaction status
finalize where the lifecycle says the flow is not complete yet
That rule applies across the current funding flows even though the exact response shape differs between deposit, withdraw, and transfer-between-buckets.
7. Working with Yokefellow primitives
The SDK is easiest to use when you think in terms of Yokefellow primitives rather than in terms of unrelated method calls. The client surface is not a grab bag of endpoints. It is a packaged way to work with the same core objects the platform itself is built around. In practice, the most important primitives for builders are buckets, offerings, rights-related outputs, and transaction flows. This section explains how the SDK maps to those primitives so a builder can choose the right methods without losing the larger system shape.
7.1 Buckets
Buckets are the main participation surfaces most apps start from. In the SDK, bucket-facing work usually begins with reads such as getBucketCatalog(...) and getBucketFunding(...). Those methods are how an app reads the public or wallet-scoped state of a bucket without rebuilding the platform model from raw service calls.
A builder should treat bucket methods as the SDK surface for answering questions like:
what does this bucket currently expose
what participation state should this app render
what funding state is attached to this bucket
what has changed after a user action
That is why bucket reads are usually the first real reads an app performs. They give the integration a live participation surface to anchor to rather than forcing the app to start from detached objects.
7.2 Offerings
Offerings are the structured participation paths inside a bucket. In the SDK, offering work appears most clearly in methods such as createBucketOfferingRequest(...), buyBucketOffering(...), and craftBucketOffering(...). These methods are not generic commerce helpers. They are the packaged participation writes for the current offering surface.
A builder should treat offering methods as the SDK surface for answering questions like:
how does a user enter this participation path
is this offering request-based, direct-buy, or craft-based in practice
what returned state should the app read after the offering action
which follow-up read confirms what the action became
That is the important mapping: offerings are not only things you display. They are the structured paths your app invokes through the write surface.
7.3 Rights-related outputs
The SDK Guide is not the place to restate the full rights model, but builders still need to know where rights-related state appears in practice. In the current surface, that state is usually read through wallet-scoped and request-scoped methods such as getWalletEntitlements(...) and getBucketOfferingRequests(...). These are the methods that let an app inspect what a wallet currently holds, what is pending, and what participation has already resolved into readable output state.
That means the SDK does not map “rights” to one single method. Instead, it exposes the states through which rights-related outputs become visible:
wallet entitlements
offering request state
bucket catalog state where relevant
queue or lifecycle state where resolution is still pending
A builder should read those surfaces together rather than expecting one universal “get rights” call to explain the whole system.
7.4 Event and queue flows
The current SDK is not only a wrapper around reads, purchases, and funding writes. It also exposes app-facing earned-event submission and queue-processing helpers.
That matters because some real Yokefellow app surfaces do not begin with a buy button. They begin with an app event that may resolve into request state, mint work, or updated entitlements, and they may include queue work that still needs to be processed visibly afterward.
7.5 Transaction flows
Transaction flows are where the SDK maps most clearly to Yokefellow’s lifecycle model. Methods such as createDeposit(...), createWithdraw(...), createTransferBetweenBuckets(...), getTransactionStatus(...), and finalizeTransaction(...) are not isolated utility calls. Together, they form the packaged surface for tracked transaction movement inside the platform.
A builder should treat transaction methods as the SDK surface for answering questions like:
how is this action prepared
what wallet-side or chain-side step still has to happen
how does the app confirm the tracked status
when is the action actually complete from Yokefellow’s perspective
That is the main reason transaction lifecycle is its own section in this guide. The methods matter individually, but they matter even more as one connected flow.
7.6 Practical reading rule
The simplest way to use the SDK is to begin with the primitive your app is actually built around.
If the app is bucket-centered, start with bucket reads. If the app is participation-centered, start with offering writes and their confirming reads. If the app is user-state-centered, start with wallet entitlements and request state. If the app is funding-centered, start with the transaction flow methods and their lifecycle helpers.
That is the practical value of the SDK: it lets builders work with Yokefellow’s real primitives through one client surface instead of rediscovering the model call by call.
8. Error handling
The SDK is built to fail loudly when a request does not succeed. In the current client surface, the default behavior is not to quietly return a half-valid payload and leave the app guessing. The client checks the HTTP response and the returned JSON shape, and if the result is not successful it throws. That means your app should treat SDK calls as operations that can fail at the request layer, the auth layer, or the lifecycle layer, and it should surface those failures clearly instead of letting them blur into normal state.
8.1 How the SDK fails
At the client level, the current SDK throws when:
the HTTP response is not successful
the returned payload indicates failure
the expected data is missing from the response
That matters because a builder should not write against the SDK as if every call always returns a usable object. Reads, writes, and lifecycle helpers should all be handled inside explicit error paths.
A normal pattern looks like this:
try { const catalog = await sdk.getBucketCatalog("your-bucket-id"); console.log(catalog); } catch (error) { console.error("Failed to read bucket catalog:", error); }
That is a better default than assuming the returned value is always safe to use immediately.
8.2 Read errors
Read errors are the first failures most integrations will hit. In practice, they usually come from:
invalid or missing identifiers
wrong environment or base URL
missing auth where the route requires it
assuming a public read is app-scoped or vice versa
Example: bucket not found
try { await sdk.getBucketCatalog("not-a-real-bucket-id"); } catch (error) { console.error("Expected read failure:", error); }
Example: app-auth failure
const sdk = createYfSdkClient({ baseUrl: "https://yokefellow.io/api/sdk/v1", appKey: "bad-key", });
try { await sdk.getAppSession(); } catch (error) { console.error("Expected auth failure:", error); }
Your app should surface the difference between “no data yet,” “object not found,” and “access failed.” Those are different states and they should not collapse into one generic empty UI.
8.3 Write errors
Write errors matter more because they happen at the point where the app is trying to change state rather than only read it. In practice, common write failures include:
bad or missing offering ids
bad bucket ids
invalid wallet context
missing auth
invalid amount formatting
trying to continue a lifecycle without the fields the next step requires
Example: offering request failure
try { await sdk.createBucketOfferingRequest("your-bucket-id", { wallet: "0xYourWalletAddress", offeringId: "not-a-real-offering-id", selectedOutputId: null, }); } catch (error) { console.error("Expected request failure:", error); }
Example: transaction status lookup failure
try { await sdk.getTransactionStatus({ txHash: "0xdeadbeef", kind: "deposit", bucketId: "your-bucket-id", }); } catch (error) { console.error("Expected tx lookup failure:", error); }
The important rule is that your app should not continue the flow as if the write succeeded. If a write fails, the UI and local app state should still reflect that the lifecycle has not advanced.
8.4 Lifecycle errors
Lifecycle errors are different from simple request failures. A lifecycle call may succeed at one step and still leave the overall flow incomplete. That is especially important in deposit, withdraw, and transfer-between-buckets flows.
In practice, lifecycle failures often come from:
missing transaction hash
wrong lifecycle kind
missing follow-up signature fields
skipping status checks
assuming finalization is optional when the returned flow still requires it
That means your app should not treat lifecycle helpers as decoration around the main action. They are part of the action. A deposit flow that was prepared but never confirmed or finalized where required is not complete from the platform’s perspective.
8.5 What good SDK error handling looks like
A good integration should do four things consistently:
show that an error happened
keep the failing operation from being treated as successful
preserve enough information to retry or inspect the failure
keep the user-facing state honest about what completed and what did not
That means a builder should usually keep or log:
the method that failed
the identifiers involved
the lifecycle kind where relevant
the returned error message
the current step of the flow
A simple pattern looks like this:
try { const result = await sdk.createDeposit({ bucketId: "your-bucket-id", wallet: "0xYourWalletAddress", amountYesRaw: "1000000000000000000", });
console.log(result); } catch (error) { console.error("Deposit preparation failed:", { bucketId: "your-bucket-id", wallet: "0xYourWalletAddress", error, }); }
That is enough to keep the failure inspectable without pretending the flow succeeded.
8.6 Practical rule
Treat every SDK call as something that can fail, and treat every lifecycle step as something that may still be incomplete until the returned state says otherwise.
For reads, surface the failure clearly. For writes, stop the flow cleanly. For lifecycle actions, confirm status before treating the result as final.
That is the safest way to build against the current SDK surface.
9. Where to go next
Once the SDK is installed, the client is working, and your app can handle the main read, write, lifecycle, and error paths cleanly, the next document depends on the kind of work you are doing. The SDK Guide is the packaged-client view of Yokefellow. The rest of the docs branch out from there depending on whether you need direct endpoint lookup, full integration structure, or a concrete reference implementation.
Go to API Reference if you need direct lookup detail for the underlying read and write surface, including object groupings, request and response patterns, and the lower-level details that sit underneath the SDK methods. That is the right next step when your team is implementing against raw service behavior rather than only against the packaged client surface.
Go to Integration Playbook if your app is moving beyond isolated calls and into real product structure. That is the right next step when you need to decide how buckets, offerings, outputs, lifecycle handling, operator-aware flows, and production assumptions fit together inside one app or partner integration.
Go to Example App Spec if you want to see the engine expressed through one full reference implementation. That is the right next step when your team understands the methods but wants to see how the pieces fit together across one believable app surface from first read through lifecycle completion.
If your work is no longer about using the SDK and is instead about broader product meaning, user expectations, system behavior, or contract structure, the right document is elsewhere in the launch set. The Whitepaper explains the product case, the Mechanics Paper explains the operating model, the Rights & Offerings Paper explains what users receive, and the Architecture / Smart Contract Spec covers contract-level structure and trust boundaries.
At that point, the SDK Guide has done its job. It has shown how the current client is created, how auth and wallet context work, how the main read and write methods are grouped, how lifecycle handling fits into the flow, and how failures should be surfaced. The next layer of docs can then go deeper from a working foundation instead of from abstraction.
