import { createDerivedAccountStore } from '@adcp/sdk/server';
const accounts = createDerivedAccountStore<AudioStackAccountMeta>({
toAccount: async (ctx) => {
const workspace = await audiostack.currentWorkspace(ctx?.authInfo);
return {
id: workspace.id, // the id buyers will send back
name: workspace.name,
status: 'active',
ctx_metadata: {}, // tokens stay on ctx.authInfo, not here
};
},
});
const accounts = createDerivedAccountStore<{ upstreamId: string }>({
listAccounts: async (ctx) => {
const rows = await meta.adAccountsFor(ctx?.authInfo); // credential-scoped
return rows.map(r => ({
id: r.account_id,
name: r.name,
status: r.disabled ? 'suspended' : 'active',
ctx_metadata: { upstreamId: r.id },
}));
},
});
const accounts = createDerivedAccountStore({
listAccounts: (ctx) => upstream.page(ctx?.authInfo),
// MUST filter by the caller, not just by id:
lookupAccount: (id, ctx) => upstream.accountForCaller(id, ctx?.authInfo),
});
const accounts: AccountStore<MyMeta> = {
...createDerivedAccountStore({ listAccounts }),
// Entries reaching this are `account: { account_id }`-keyed AND already
// resolved against the caller's reachable set — the framework refuses
// natural-key provisioning and unreachable ids for 'derived'. Returned
// rows must still carry `brand` + `operator` (schema-required); echo
// them from your own account record.
upsert: async (refs, ctx) => myUpstream.updateSettings(refs, ctx),
};
Build an
AccountStore<TCtxMeta>for an agent fronting an upstream-managed account namespace.The factory:
resolution: 'derived'.AdcpError('AUTH_REQUIRED')whenctx.authInfocarries no credential (skip withskipAuthCheck: true). The check accepts the discriminatedcredentialshape (preferred) AND the deprecatedtoken/clientIdfields populated by pre-#1269 authenticators — fail-closed only when none of the three are present.account_idagainst what the credential can reach, and returnsnullon any miss so the framework emits the spec's fixedACCOUNT_NOT_FOUNDenvelope. Verification is performed by the factory, not delegated to adopter code — an adopter cannot forget it and silently serve cross-tenant requests.list_creative_formats,provide_performance_feedback, …) when the credential reaches exactly one account; returnsnullwhen it reaches several, because "whichever one" is not a defensible default.list(list_accounts) — required for'derived'platforms, and the only way buyers learn ids. Honors the wire filters the framework passes through (account,status,sandbox) and pages the result (pagination.max_results, capped at 100, with an opaque cursor).upsert. Natural-key provisioning is out of scope for this mode (the framework fails thosesync_accountsentries per-row withUNSUPPORTED_PROVISIONING); adopters whose upstream supports settings-update writes composeupserton top via spread.Refuses the brand+operator arm. The framework rejects
{ brand, operator }references for'derived'platforms withAdcpError('INVALID_REQUEST', { field: 'account.brand' })before reaching this resolver, pointing the buyer atlist_accounts. The factory also returnsnullfor such refs defensively.