Cross-service walkthrough
End-to-end narrative: signup → deposit → payment → group → deal. Touches all three services + the federated gateway.
A single narrative across auth, payments-direct, agents, and the federated gateway: authenticate off-chain, activate one chain account only when it is needed, move value on the matching payments instance, and use federation for cross-subgraph data.
What you'll build
- Sign up + log in (auth)
- Select a chain + activate its account (auth → payments)
- Deposit + send a confidential payment (payments-direct)
- Create a working group + post a thread (agents)
- Read deal-flow data through federation (gateway → agents/shared core)
Prerequisites
- Auth REST at
auth.yieldfabric.com. - The GraphQL gateway at
api.yieldfabric.com. - Test payments-direct GraphQL and message polling at
pay.test.yieldfabric.com(Live usespay.live.yieldfabric.com). - Agents REST/SSE at
agents.yieldfabric.com. curlandjq.
Step 1 — Auth: sign up + log in
User creation and login are off-chain. They create identity/key material and a chain-bound session, but no smart account:
curl -s -X POST https://auth.yieldfabric.com/auth/users \
-H 'Content-Type: application/json' \
-d '{"email":"demo@yieldfabric.com","password":"correct horse battery staple"}' \
| jq .
LOGIN=$(curl -s -X POST https://auth.yieldfabric.com/auth/login \
-H 'Content-Type: application/json' \
-d '{"email":"demo@yieldfabric.com","password":"correct horse battery staple"}')
TOKEN=$(echo "$LOGIN" | jq -r '.token // .access_token')
REFRESH=$(echo "$LOGIN" | jq -r '.refresh_token')
USER_ID=$(echo "$LOGIN" | jq -r '.user.id')
Per-flow detail: Authentication & signing.
Step 2 — Explicit chain-account activation (auth → payments)
Select an enabled chain through refresh. Refresh tokens rotate, so retain the replacement:
CHAIN_ID=153
MODE_SESSION=$(curl -s -X POST https://auth.yieldfabric.com/auth/refresh \
-H 'Content-Type: application/json' \
-d "{\"refresh_token\":\"$REFRESH\",\"chain_id\":\"$CHAIN_ID\"}")
TOKEN=$(echo "$MODE_SESSION" | jq -r '.access_token // .token')
REFRESH=$(echo "$MODE_SESSION" | jq -r '.refresh_token')
Activation is an explicit, idempotent resource. The caller can activate their own user or a group they own/administer; it cannot activate another user:
ACTIVATION_URL="https://auth.yieldfabric.com/entities/user/$USER_ID/chain-accounts/$CHAIN_ID/activation"
ACTIVATION=$(curl -s -X POST "$ACTIVATION_URL" \
-H "Authorization: Bearer $TOKEN" \
-H 'Content-Type: application/json' \
-d '{}')
echo "$ACTIVATION" | jq .
The lifecycle is provisioning, pending_signature, ready, or
failed_retryable. Poll with GET while provisioning. A
failed_retryable state may be POSTed again to begin/reconcile the next
attempt; pending_signature requires the wallet signing ceremony. Continue
only after ready:
curl -s "$ACTIVATION_URL" \
-H "Authorization: Bearer $TOKEN" \
| jq '{status, chain_id, wallet_id, account_address}'
Refresh once more on the same explicit chain after readiness so the JWT
carries the new default_wallet_id and account_address:
READY_SESSION=$(curl -s -X POST https://auth.yieldfabric.com/auth/refresh \
-H 'Content-Type: application/json' \
-d "{\"refresh_token\":\"$REFRESH\",\"chain_id\":\"$CHAIN_ID\"}")
TOKEN=$(echo "$READY_SESSION" | jq -r '.access_token // .token')
REFRESH=$(echo "$READY_SESSION" | jq -r '.refresh_token')
Step 3 — Payments-direct: deposit + send + settle
Wallet/MQ/RPC-backed operations go to the payments instance selected by the JWT chain. For chain 153 in this example:
PAY_URL=https://pay.test.yieldfabric.com
DEPOSIT=$(curl -s -X POST "$PAY_URL/graphql" \
-H "Authorization: Bearer $TOKEN" \
-H 'Content-Type: application/json' \
-d '{
"query": "mutation($input: DepositInput!) { deposit(input: $input) { success messageId } }",
"variables": { "input": {
"assetId": "aud-token-asset",
"amount": "100",
"idempotencyKey": "demo-deposit-1"
}}
}')
echo "$DEPOSIT" | jq .
DEPOSIT_MESSAGE_ID=$(echo "$DEPOSIT" | jq -r '.data.deposit.messageId')
Send a confidential payment. destinationId is the recipient's entity or
wallet id:
SEND=$(curl -s -X POST "$PAY_URL/graphql" \
-H "Authorization: Bearer $TOKEN" \
-H 'Content-Type: application/json' \
-d '{
"query": "mutation($input: InstantSendInput!) { instant(input: $input) { success messageId transactionId } }",
"variables": { "input": {
"destinationId": "ENT-…",
"assetId": "aud-token-asset",
"amount": "10",
"idempotencyKey": "demo-send-1"
}}
}')
echo "$SEND" | jq .
SEND_MESSAGE_ID=$(echo "$SEND" | jq -r '.data.instant.messageId')
GraphQL mutation success means the MQ accepted the work, not necessarily that
the chain transaction settled. Poll each message id on the same payments
host until executed is non-null. If the next step reads graph-materialized
rows, also wait for post_processed_at:
curl -s "$PAY_URL/api/users/$USER_ID/messages/$SEND_MESSAGE_ID" \
-H "Authorization: Bearer $TOKEN" \
| jq '{executed, post_processed_at, response_status}'
Payments mutations do not go through federation in a dual deployment. The gateway is pinned to one payments subgraph for composition; direct Test/Live routing follows the JWT-selected host.
Per-mutation detail: Payments.
Step 4 — Agents: working group + thread
GROUP_ID=$(curl -s -X POST https://agents.yieldfabric.com/working-groups \
-H "Authorization: Bearer $TOKEN" \
-H 'Content-Type: application/json' \
-d '{"name":"Demo group","description":"Cross-service walkthrough"}' \
| jq -r .id)
THREAD_ID=$(curl -s -X POST \
"https://agents.yieldfabric.com/working-groups/$GROUP_ID/threads" \
-H "Authorization: Bearer $TOKEN" \
-H 'Content-Type: application/json' \
-d '{"title":"First thread"}' \
| jq -r .id)
curl -s -X POST \
"https://agents.yieldfabric.com/working-groups/$GROUP_ID/threads/$THREAD_ID/messages" \
-H "Authorization: Bearer $TOKEN" \
-H 'Content-Type: application/json' \
-d '{"text":"Hello from the walkthrough"}' \
| jq .
Agent runtime, working-group model, and threads: Agents, knowledge & workspaces.
Step 5 — Federated GraphQL: cross-subgraph/shared-core reads
Use the gateway where federation adds value: a query that joins or exposes auth, agents, and shared-core entities. Deal-flow is owned by agents and is reachable through the gateway:
curl -s -X POST https://api.yieldfabric.com/graphql \
-H "Authorization: Bearer $TOKEN" \
-H 'Content-Type: application/json' \
-d '{
"query": "query { dealFlow { dealsAwaitingMySignature { id status name } } }"
}' \
| jq .
An empty list is expected for a fresh user. Payments-instance mutations and mode-bound payments reads remain payments-direct even when an agents workflow initiates them internally.
Service boundaries you just crossed
All surfaces accept the same user JWT, but its default_chain_id determines
which payments-direct host may execute mode-bound work.
sequenceDiagram
autonumber
participant C as Client
participant Auth as auth.yieldfabric.com
participant Pay as pay.test.yieldfabric.com
participant Agents as agents.yieldfabric.com
participant Router as api.yieldfabric.com
C->>Auth: POST /auth/users + /auth/login
Auth-->>C: Walletless JWT + refresh token
C->>Auth: POST /auth/refresh (chain 153)
C->>Auth: POST /entities/user/:id/chain-accounts/153/activation
Auth->>Pay: DeployAccount on chain 153
Auth-->>C: provisioning / ready
C->>Auth: POST /auth/refresh (chain 153)
Auth-->>C: JWT with chain account
C->>Pay: POST /graphql (deposit/send)
Pay-->>C: messageId
C->>Pay: GET /api/users/:user/messages/:message
Pay-->>C: execution + post-processing state
C->>Agents: POST /working-groups/:id/threads
Agents->>Auth: Validate JWT
Agents-->>C: Thread state
C->>Router: Federated dealFlow query
Router->>Agents: Resolve agents-owned field
Agents-->>C: Shared response
Failure modes across services
409 on user createprovisioningpending_signatureCHAIN_ACCOUNT_REQUIREDCHAIN_MODE_MISMATCHexecuted time403 posting a message