@adcp/sdk API Reference - v7.9.0
    Preparing search index...

    Interface OAuthPassthroughResolverOptions<TUpstreamRow, TCtxMeta>

    interface OAuthPassthroughResolverOptions<
        TUpstreamRow,
        TCtxMeta = Record<string, unknown>,
    > {
        httpClient: UpstreamHttpClient;
        listEndpoint: string;
        idField?: keyof TUpstreamRow & string;
        rowsPath?: string | null;
        getAuthContext?: (
            ctx: ResolveContext | undefined,
        ) => AuthContext | undefined;
        toAccount: (
            row: TUpstreamRow,
            ctx: ResolveContext | undefined,
        ) => Account<TCtxMeta>;
        cache?: {
            ttlMs: number;
            getCacheKey?: (authContext: AuthContext | undefined) => string;
            maxEntries?: number;
        };
    }

    Type Parameters

    • TUpstreamRow
    • TCtxMeta = Record<string, unknown>
    Index

    Properties

    httpClient: UpstreamHttpClient

    Pre-configured upstream HTTP client (typically from createUpstreamHttpClient). Should be configured with auth: { kind: 'dynamic_bearer', getToken: (ctx) => ... } so the factory's getAuthContext output flows through to bearer selection.

    listEndpoint: string

    Path on the upstream API that returns the buyer's accounts. Common shapes: /v1/adaccounts, /me/adaccounts, /customers.

    idField?: keyof TUpstreamRow & string

    Property on each upstream row matching the wire AccountReference.account_id. Defaults to 'id'.

    Footgun: when the generic TUpstreamRow is inferred (no explicit type argument), keyof TUpstreamRow & string collapses to string and a typo in idField compiles fine but silently always returns null. Pass an explicit TUpstreamRow (or a dummy interface with the upstream's field names) to get compile-time field validation.

    rowsPath?: string | null

    Property on the upstream response body that contains the array of rows. Defaults to 'data' (Snap, Meta envelope shape). Set to null when the response body itself is the array (some flat-list APIs).

    Single-segment only. APIs with deeper nesting (TikTok's data.list, for example) need a custom response transform — wrap the upstream client with a fetch override that flattens, or use this factory only for the 70%-fit upstream shape and write a hand-rolled resolver for the rest. Google Ads' customers:listAccessibleCustomers returns string resource names (not row objects) and is not a fit for this factory.

    getAuthContext?: (ctx: ResolveContext | undefined) => AuthContext | undefined

    Extract the auth context to forward to the upstream's dynamic_bearer.getToken(ctx) resolver. The return value flows through as the authContext per-call option on httpClient.get.

    Defaults to forwarding ctx?.authInfo verbatim — works when the http client's getToken resolver reads ctx.authInfo.token / ctx.authInfo.credential.token.

    toAccount: (
        row: TUpstreamRow,
        ctx: ResolveContext | undefined,
    ) => Account<TCtxMeta>

    Map an upstream row to a framework Account<TCtxMeta>. Adapters typically embed the raw row (or distilled fields) in ctx_metadata so downstream specialism methods can read upstream IDs, tokens, etc. without re-resolving.

    Treat ctx_metadata.accessToken (or any embedded credential) as a secret. The framework strips ctx_metadata from the wire response, but adopter code that throws an error containing JSON.stringify(account) or logs ctx.account at info level WILL leak the token. Either don't embed the bearer (re-derive from ctx.authInfo on each downstream method), or audit your error projections.

    cache?: {
        ttlMs: number;
        getCacheKey?: (authContext: AuthContext | undefined) => string;
        maxEntries?: number;
    }

    Optional in-memory listing cache. Caches the full row array per buyer (keyed on the auth context); resolves account_id matches in-memory within the TTL window. One upstream hit per buyer per TTL period regardless of how many account_ids that buyer queries.

    Set ttlMs to enable (default: no cache, every resolve hits upstream).

    TTL guidance:

    • Read tools (get_products, list_creative_formats): 60–300s. Buyer-side latency dominates; longer TTL absorbs bursts.
    • Mutating tools (create_media_buy, update_media_buy, sync_creatives): 0–30s, or compose composeMethod to skip cache on these paths. A revoked-but-not-yet-expired bearer would otherwise continue authorizing mutations for the full TTL window.

    getCacheKey defaults to a SHA-256 hash of the JSON-serialized auth context. Hashing (vs raw stringification) keeps the bearer token out of Map keys — heap dumps and util.inspect(cache) no longer surface plaintext credentials. Provide a custom key when the auth context contains noise (timestamps, request ids) that would defeat caching.

    Cache size is naturally bounded by the number of distinct buyers times one entry each. Adopters with token-rotation churn can cap with maxEntries (LRU eviction).

    Type Declaration

    • ttlMs: number
    • OptionalgetCacheKey?: (authContext: AuthContext | undefined) => string
    • OptionalmaxEntries?: number

      LRU eviction cap. Defaults to 1024 — enough for most multi-tenant deployments.