Yokefellow - Builder Docs
Quickstart
The smallest practical path from setup to a first successful read, controlled action, and lifecycle confirmation.

The purpose of this quickstart is to get a builder from setup to first successful integration with the fewest moving parts. It is the smallest practical path through the Yokefellow integration surface: prepare your environment, choose the SDK or API path, read a live object, run one controlled action, and confirm the resulting state. That is the role the launch map gives this document inside the Developer Docs Hub.
1. What this quickstart gives you
This quickstart gives you a first working path through the system, not the full integration playbook. By the end, a builder should be able to connect to the Yokefellow surface they have access to, read a real object such as a bucket or offering, execute one small controlled flow, and verify that the resulting state is returned and handled correctly.
The point is not to cover every primitive at once. The point is to prove that your environment, your integration path, and your lifecycle handling are all working before you move on to deeper implementation work. Once that succeeds, the SDK Guide, API Reference, and Integration Playbook become much easier to use well.
2. Before you begin
Before starting, make sure you know which access posture applies to your build. Yokefellow’s builder surface is real, but builder access begins controlled rather than fully open. In practice, that means this quickstart is written for the people already building against the surface now: internal teams, approved partners, and later approved builders as the platform expands.
You should also decide up front whether you are taking the SDK path or the API path. Both are valid, but they are not the same working style. The SDK path is usually the better starting point when you want the packaged developer surface and the platform’s intended integration flow. The API path is the better starting point when you need direct programmatic control over reads, writes, and returned objects.
Finally, know whether your first test is read-only or whether it includes a write or transaction flow. A read-only start is the safest first proof that your environment is wired correctly. A write path is the next step, and it should be done in a controlled environment where you can verify the result cleanly rather than guessing from the UI afterward. Also keep three things separate from the start: request auth proves the integration, wallet context identifies the user-side state, and wallet signing or transaction submission completes the chain-side step where required.
3. Choose your path
3.1 SDK path
Choose the SDK path if you want to work through Yokefellow’s packaged integration surface. This is usually the best way to start when you want a more guided builder experience and when you expect to follow the platform’s intended lifecycle patterns rather than build directly against raw endpoints from the beginning.
The SDK path is usually the fastest route to a working first integration because it groups the common developer surface in one place: setup, signing assumptions, lifecycle handling, and the main primitives builders touch in code.
3.2 API path
Choose the API path if you want to work directly with the read and write interface. This is the better path when you need a more reference-driven approach, when you want tighter control over how calls are made, or when your integration architecture is built around direct service interaction rather than around a packaged client surface.
The API path usually becomes more useful once you know the shape of the objects you are reading and writing, but it is also a valid first path when your integration is built around direct route-level control from the start.
4. Configure your environment
Your first job is to make sure the integration is pointed at the correct versioned Yokefellow environment and that your app can satisfy the request-auth, wallet-context, and signing assumptions required by the path you chose. You do not need the full production setup to begin, but you do need a working environment that can successfully read live Yokefellow data and, when appropriate, prepare and submit a controlled action correctly. In the current surface, builders are working against the versioned /api/sdk/v1 family, and the practical setup rule is to prove the environment with one real call before building larger flows.
For the SDK path, that usually means creating one client pointed at the versioned Yokefellow base route and supplying app auth only where the route needs it. For the direct API path, it means calling the same versioned route family yourself and attaching the correct auth headers for gated routes instead of assuming every request uses the same access posture. In both cases, the goal is not to wire the whole product first. The goal is to prove that your integration can successfully reach the real Yokefellow surface it is meant to use.
At this stage, keep the work narrow. Do not begin with broad feature wiring. Do not begin with UI polish. Get the connection, the environment settings, and the first primitive read working first. That gives the rest of the quickstart something real to anchor to.
Also keep three things separate from the start. Request authentication proves the integration can access the route it is calling. Wallet context identifies which user-side state the read or action is about. Wallet signing or transaction submission completes the chain-side step where required. These layers are related, but they are not the same proof and should not be treated as interchangeable.
Once your environment is ready, the next step is to read one real object from the system. A bucket is usually the best first anchor because it gives the integration a live participation surface to work against. An offering is also a good first read when the integration is centered more narrowly on participation flows. A public bucket read is a good first proof that the route is reachable and that your app can handle a real returned object. If you also need to prove authenticated integration recognition before moving into gated writes, use the app-session read for that proof rather than treating the first public bucket read as an auth check.
5. First read
Start with one real read against a live bucket. In the current surface, the cleanest first example is reading a bucket catalog. That remains the best first anchor because it gives the app one live participation surface to work against instead of forcing the builder to assemble the first screen from scattered calls.
SDK example
import { createYfSdkClient } from "@yokefellow/sdk-client"; const sdk = createYfSdkClient({ baseUrl: "https://your-domain.com/api/sdk/v1", appKey: process.env.YF_APP_KEY, }); const bucketId = "your-bucket-id"; const catalog = await sdk.getBucketCatalog(bucketId); console.log("Bucket catalog:", catalog);
This is a good first read because it proves four things at once:
your environment is pointed at the correct Yokefellow surface
the route is reachable
your app can request a real bucket object
your integration can inspect a real returned shape instead of a mock
A bucket catalog read is a public-first read. It is a strong first proof that your environment is wired correctly, but it should not be treated as the proof that authenticated integration recognition is working.
If your integration is user-specific, read the same bucket with wallet context:
const wallet = "0xYourWalletAddress"; const catalog = await sdk.getBucketCatalog(bucketId, { wallet }); console.log("Wallet-aware bucket catalog:", catalog);
Wallet context is separate from request authentication. Passing a wallet asks Yokefellow to return wallet-aware state for the same bucket. It does not, by itself, prove that the integration is on an authenticated app-scoped route.
Direct API example
const bucketId = "your-bucket-id";
const response = await fetch(
https://yokefellow.io/api/sdk/v1/buckets/${bucketId}/catalog,
{ cache: "no-store" }
);
const payload = await response.json();
if (!response.ok || !payload.ok) {
throw new Error(payload?.error?.message || "Failed to read bucket catalog");
}
console.log(payload);
A good first-read success case is simple: your app can request a real bucket, receive a valid response, and render or log that response cleanly. The bucket catalog is broad enough to act as a real integration anchor rather than only a narrow object lookup.
If you also need to prove authenticated integration recognition before moving into gated writes, use the app-session read for that proof:
const session = await sdk.getAppSession(); console.log("App session:", session);
That is the cleaner auth check because a successful public bucket read proves reachability, while a successful app-session read proves that the integration is being recognized on an authenticated route.
6. First write or transaction flow
There are two good first write examples, depending on what you want to prove first.
Option A: App-level flow — create an offering request
This is the better first example when you want a controlled write without starting with chain-backed funding. It creates real participation state, stays inside the structured offering surface, and gives you a clean confirming read through request history afterward.
const bucketId = "your-bucket-id"; const wallet = "0xYourWalletAddress"; const offeringId = "your-offering-id"; const result = await sdk.createBucketOfferingRequest(bucketId, { wallet, offeringId, selectedOutputId: null, }); console.log("Created offering request:", result);
This is a strong first write because it gives you:
a real state-changing action
a clear before-state and after-state
an easy way to confirm the result through the request history
If you are implementing directly against the API instead of the SDK, the same path is the bucket offering-request route under the versioned /api/sdk/v1 surface. In the current controlled-access posture, that usually means sending the request with an app-auth header and a JSON body that carries the wallet and offering context.
const baseUrl = "https://yokefellow.io/api/sdk/v1";
const bucketId = "your-bucket-id";
const wallet = "0xYourWalletAddress";
const offeringId = "your-offering-id";
const response = await fetch(
${baseUrl}/buckets/${encodeURIComponent(bucketId)}/offering-requests,
{
method: "POST",
headers: {
"Content-Type": "application/json",
"x-yf-app-key": process.env.YF_APP_KEY ?? "",
},
body: JSON.stringify({
wallet,
offeringId,
selectedOutputId: null,
}),
}
);
const payload = await response.json();
if (!response.ok || !payload?.ok) { throw new Error( payload?.error?.message || "Failed to create offering request" ); } console.log("Created offering request:", payload);
If your integration uses bearer auth instead of an app key, send the bearer header for the same route instead of the app-key header.
Option B: Chain-backed flow — prepare and submit a deposit
This is the better first example when you want to prove your funding lifecycle and transaction handling. It is the more representative Yokefellow transaction flow, but it is not one step. The prepare response tells your app what still needs to happen next on the wallet side.
const bucketId = "your-bucket-id"; const wallet = "0xYourWalletAddress"; // 1 YES in raw 18-decimal units const amountYesRaw = "1000000000000000000"; const prepared = await sdk.createDeposit({ bucketId, wallet, amountYesRaw, }); console.log("Prepared deposit:", prepared);
At this point, do not treat the deposit as complete. The prepared response can include an approval descriptor and a deposit transaction descriptor. Your app should inspect that response, complete any required wallet-side approval, then submit the deposit transaction through the wallet path before moving on to status and finalization.
A practical shape looks like this:
const approval = prepared.onchain?.approval; const transaction = prepared.onchain?.transaction; // If approval is required, submit the approval transaction first. // Then submit the deposit transaction through the wallet using // the returned contract address, calldata, and value fields. console.log("Approval descriptor:", approval); console.log("Deposit transaction descriptor:", transaction);
A deposit is a stronger lifecycle example because it forces your integration to deal with more than one real step: preparation, wallet-side approval where required, wallet-side transaction submission, and later status tracking or finalization. That is the important rule for this section: request authentication, wallet context, and wallet signing are related, but they are not the same thing, and a prepared deposit is not a finished deposit.
If you want the quickest win, use offering request first.
If you want the most representative Yokefellow transaction flow, use deposit first.
7. Confirm the lifecycle result
Do not stop at “the call returned.” Confirm the resulting state through the actual surface your app is supposed to support. A successful write is only one step in the product flow. The real check is whether the resulting state appears where your integration is meant to read it afterward. That is the same rule the Quickstart, SDK Guide, and Integration Playbook all support: confirm the result through the surface that actually matters to the feature, then refresh the primary bucket-facing or wallet-facing state your app depends on.
Confirm an offering request
If you used createBucketOfferingRequest, confirm it by reading the bucket’s request history.
const requests = await sdk.getBucketOfferingRequests(bucketId, { wallet, limit: 10, }); console.log("Offering requests:", requests);
That gives you a real before-and-after pattern:
before: no request in the list
action: create request
after: the request appears in the returned request history
That is a clean first lifecycle proof because it confirms the write through the surface that actually holds request state rather than assuming the create response alone is enough. After that, refresh the bucket-centered or wallet-centered surface your app uses so the visible product state stays honest.
Confirm a deposit lifecycle
If you used createDeposit, confirm the transaction through the tx-status surface.
const txHash = "0xYourTransactionHash"; const status = await sdk.getTransactionStatus({ txHash, kind: "deposit", bucketId, }); console.log("Deposit status:", status);
If your deposit flow requires explicit finalization, use the finalize surface:
const finalized = await sdk.finalizeTransaction({ kind: "deposit", txHash, bucketId, wallet, }); console.log("Finalize result:", finalized);
For a chain-backed flow, the integration should be able to answer four simple questions:
did the wallet-side transaction submit
did Yokefellow recognize the transaction
did finalization complete or remain pending
can the app surface that state clearly
That is the real lifecycle test. A confirmed chain transaction and a fully reflected platform result are not always the same moment, so the app should be built to show pending, recognized, finalized, or still-catching-up state honestly rather than collapsing everything into one instant-success message. Once the lifecycle state is confirmed, refresh the funding, bucket, or wallet-facing surface your feature actually uses.
8. Handle the first expected error
Do not leave the quickstart on the happy path. Add one error case immediately. The goal of the first error-handling pass is not to simulate every failure. It is to prove that your integration can recognize a failure honestly, stop the flow where it should stop, and preserve enough information to recover or retry when that is actually appropriate. The current API and SDK surfaces use structured errors, and the validation layer checks things like UUIDs, wallet addresses, and transaction hashes before route logic fully runs. That means your first expected errors should be named honestly instead of being described as not-found cases when the real failure is malformed input.
Example: invalid bucket id format
try { await sdk.getBucketCatalog("not-a-real-bucket-id"); } catch (error) { console.error("Expected bucket-id validation failure:", error); }
This is a useful first read failure, but it is a validation example, not a bucket-not-found example. The bucket id is malformed, so the integration should treat it as invalid input and stop cleanly rather than continuing as if a real bucket lookup happened.
If you want a true not-found example instead, use a syntactically valid bucket id that does not exist and handle that separately as an object lookup failure.
Example: invalid or missing app auth
import { createYfSdkClient } from "@yokefellow/sdk-client"; const sdk = createYfSdkClient({ baseUrl: "https://your-domain.com/api/sdk/v1", appKey: "bad-key", });
try { await sdk.getAppSession(); } catch (error) { console.error("Expected auth failure:", error); }
This is the right auth failure example because getAppSession() is the clean authenticated-recognition check in the current surface. If this fails, the app should surface it as an access problem, not as “no data yet” and not as a generic empty state.
Example: invalid transaction hash format
try { await sdk.getTransactionStatus({ txHash: "0xdeadbeef", kind: "deposit", bucketId, }); } catch (error) { console.error("Expected tx-hash validation failure:", error); }
This is also a validation example, not a transaction-not-found example. The transaction hash is malformed, so the integration should treat it as bad input rather than as a missing lifecycle record. If you want a real status-lookup miss, use a validly shaped transaction hash that does not resolve to the lifecycle state you asked for.
The first error-handling pass should prove four things:
the error is visible
the app stops where it should stop
the integration does not silently treat failure as success
retry or recovery behavior is possible where appropriate
A simple rule is enough for the quickstart: surface validation failures as invalid input, surface auth failures as access failures, surface not-found conditions as object or state absence, and keep incomplete lifecycle state separate from true failure. That is the safest first error-handling posture for a real Yokefellow integration.
9. Where to go next
Once you have completed:
one real bucket read
one controlled write
one lifecycle confirmation
one expected error case
move to the document that matches the next kind of work.
Go to SDK Guide if you want deeper use of the packaged client surface. Go to API Reference if you want direct read/write lookup detail. Go to Integration Playbook if you are wiring these flows into a real product. Go to Example App Spec if you want to see one end-to-end reference integration.
