@agentvault/playwright, and see where it behaves
differently from Playwright.
Overview
The SDK has two halves:- HTTP helpers that need no browser: the token catalog, signup and login writes, and the recipe registry.
- A proxied browser (
chromium.launch()) shaped like Playwright’s, where every call runs in the Agent Vault proxy and token fills are resolved there.
SelfAgentPayError. Branch on
its code; see Error codes. The exceptions throw native
JavaScript errors:
- Saving a screenshot to a local file with
screenshot({ path })can throw a nativeTypeErroror a filesystem error, listed underscreenshot(options?)below. recipeSubmission()rejects with the nativefetcherror on a network failure or an abort.recipesForUrl()andsubmitRecipe()reject with the native abort error when yoursignalaborts while the body is being read.listTokens(),tokensForUrl(),recipesForUrl()andbuyableMerchants()throw a nativeSyntaxErroron a successful response that is not valid JSON. A body cut off mid-read, or for the two registry helpers JSON of the wrong shape, can throw a nativeTypeError.
Exports
The module also exports
actionTimeoutFor, currentOwnerWaitObserver,
podResumeUrl, unreachable, signupPollIntervalMs, signupPollSleepMs and
signupPollTimeoutMs. They are internal helpers, not part of the documented
API, and can change without notice.
The default export carries chromium, listTokens, tokensForUrl, the signup
and login helpers (createLogin, getSignupRequest, awaitSignupApproval,
setLoginUsername, rotateLoginPassword), the registry helpers
(recipesForUrl, submitRecipe, recipeSubmission) and sdkCapabilities.
Everything else, including observingOwnerWaits, defaultIdentity,
buyableMerchants, fingerprintUrl, requestTimeoutMs and
SelfAgentPayError, is a named export only.
The checkout playbooks are a separate entry point:
@agentvault/playwright/playbooks.
Catalog helpers
listTokens(options?)
Fetch the mock tokens this key may use. Needs no browser.
string
Narrows cards and logins to those registered for this page. Cards marked
anySite: true are always kept, after the ones registered for the page. A URL
that does not parse matches no site: site-scoped cards and every login drop
out, and the anySite cards remain. Identity and addresses always come back
in full.string
Overrides
SELF_AGENT_PAY_API_KEY.string
Overrides the API base. Defaults to
SELF_AGENT_PAY_API_URL. If that is
unset, the SDK falls back to the backend URL variables that an Agent Vault
deployment’s own build sets, so code running inside a deployment needs no
value.TokenCatalog: userId, identity, addresses, cards,
logins, and relatedLogins on a URL-narrowed catalog. Never a real card
number or CVV. Full shape: Token catalog.
Errors: coded from the HTTP status. A 401 is invalid_api_key. A 429 is
rate_limited, with retryAfterSeconds when the response’s Retry-After was
usable. Any other failing status, an unreachable API and a missing API key throw
a SelfAgentPayError with no code.
tokensForUrl(url, options?)
The same filter as listTokens({ url }), in a flatter shape. Accepts the same
options, plus catalog to reuse one you already fetched. Pass only a full
catalog from listTokens() called without url. The filter runs locally, and a
catalog narrowed to one site has already lost every other site’s cards and
logins, so reusing it for a different host returns too little. For a new host,
pass a full catalog or leave catalog out to fetch again.
Returns userId, identity, addresses, cards, logins and
relatedLogins. relatedLogins names logins registered on a sibling host of
the same site, hosts only, with no tokens. They will not fill here: navigate to
that host and ask again.
defaultIdentity(catalog)
Returns the identity labelled default (the account profile), or the first
entry if there is no such label. undefined when the catalog has no identity.
Launching
chromium.launch(options?)
Open a proxied browser. This starts one proxy session; see
Sessions and timeouts.
string
The API key this session authenticates with. Overrides
SELF_AGENT_PAY_API_KEY for this launch only, so one process can open
sessions for several keys. When omitted, the environment variable is used. An
empty string does not fall back to it: with no key from either place,
launch() throws “chromium.launch: no API key.” before connecting. A resumed
session reconnects with the same key.string
The proxy’s WebSocket URL. Overrides
SELF_AGENT_PAY_PROXY_URL for this
launch only. When omitted or blank, the environment variable is used, then
ws://localhost:4000/v1, a local development proxy. For the hosted service,
set wss://proxy.agentvault.io/v1. A resumed session reconnects to the same URL.boolean
How the browser’s traffic leaves. Omitted (the default): prefer a tunnel
through your machine and fall back, with a warning, to the deployment’s own
egress: the proxy’s address, or its residential egress where one is configured.
true: require the tunnel and refuse to launch without one. false: never
tunnel. Overrides SELF_AGENT_PAY_TUNNEL. See Egress and tunnelling.boolean
default:"false"
Require residential egress without a tunnel through your machine, for a hosted
agent that has no machine of its own. The proxy lets the session in only once
its residential provider answers for the session’s country. Otherwise
launch() fails with egress_unavailable and a retryAfterSeconds. It never
falls back to the proxy’s own address. The country comes from your key’s
cards, then your saved addresses. When neither agrees on one country, the
deployment’s fallback country is used instead, which may not be yours. See
Residential egress country. If that egress
stops working mid-session, every later call except outcome() and the
close() calls fails with egress_lost: close and launch again. Defaults to
SELF_AGENT_PAY_RESIDENTIAL_EGRESS=1 when set. A working tunnel through your
machine also satisfies it.Without this option, a session with no tunnel still uses the deployment’s
residential egress when it has one; the option only makes it a requirement.
Either way the session is HTTPS only, and goto() throws insecure_site for
a site that will not load over HTTPS. See
Residential egress.number
A hard bound, in milliseconds, on each page call. A call that runs past it
rejects with
action_timeout and the session stays open, unlike a
request_timeout. The call may still be running in the proxy, so treat an
action as possibly landed. A per-call timeout does not raise it. Never
applied to fills, typing, selectOption, attestedCheckout,
requestChallengeHandoff or close(), which can wait on the owner. Defaults
to SELF_AGENT_PAY_ACTION_TIMEOUT_MS. Unset means no bound. See
The per-action bound.boolean
default:"true"
After a network drop, reconnect to the same session instead of ending it. Only
a call in flight across the drop fails, with
connection_resumed. Set false
(or SELF_AGENT_PAY_RESUME=0) to end the session on any drop. See
Resume after a dropped connection.boolean
default:"false"
Hold for the owner’s approval even when the amount you report is under the
card’s approval threshold. Defaults to
SELF_AGENT_PAY_STRICT_APPROVAL=1 when
set. A checkout whose total cannot be read always holds when a threshold is
set, with or without this option.boolean
default:"true"
Report what a challenge handoff actually did (
solved, released_unsolved)
instead of a flat completed. Set false (or
SELF_AGENT_PAY_VERIFIED_OUTCOMES=0) only if your code branches on
outcome !== "completed" and cannot be updated yet.launch() reads. The options type still accepts
Playwright’s LaunchOptions, so existing code compiles, but none of them reach
the remote browser: args, headless, proxy, timeout and the rest are
silently ignored. The proxy launches and configures its own browser.
launch() is the only method chromium has. Its type still carries the rest
of Playwright’s BrowserType, so chromium.connect() or
chromium.launchPersistentContext() compiles, but those members are undefined
at runtime.
SdkBrowser
newContext(options?)
The options type is Playwright’s BrowserContextOptions, so existing code
compiles, but nothing in it reaches the proxy. The SDK sends only the browser’s
id. storageState, locale, timezoneId, userAgent, viewport,
extraHTTPHeaders and every other context option are silently ignored. The
proxy configures the browser itself.
A context is not an isolated profile either. Every context opened from one
browser shares that browser’s single profile, so cookies, storage and sign-in
are shared between them. For a clean profile, launch a new browser.
Handoff records
takeHandoffs() returns, and recordingHandoffs() fills, a HandoffRecord:
ProxyHandoff[]
One entry per pause the proxy reported.
cvv_entry: the owner typed the
card’s security code while a fill waited. purchase_approval: the owner was
asked to approve the purchase before the card number went in.
destination_approval: the owner was asked to approve a new site for the
account’s saved details; host names the site. ms is how long the call
waited for the person. A call that failed after its pause still reports it.number
How many calls ended without a reply from the proxy: a deadline that
fired, a socket that closed, or a send that failed. Such a call may have
paused for the owner and never said so.
For
recordingHandoffs(), pass a record you created, initialized to zero.
The record is yours, so it keeps what was collected even when run throws.
Calls made inside run do not reach takeHandoffs().
requestChallengeHandoff() are not listed.
Owner waits as they happen
A handoff record says who took part once a call has finished. To know what a call is waiting on while it waits, run it insideobservingOwnerWaits(observer, run).
The observer is told each wait as it starts, and null when it ends:
It is a function, not a browser method, so it can scope work before a browser
exists. The report never carries the approval link: that goes to the owner
alone. A proxy that predates the report sends nothing, so read silence as
unknown, not as “nobody was asked”.
That call reports its own result.
outcome()
Returns a SessionOutcome: filledFieldKinds, deniedCount,
confirmationDetected, orderTotal and credentialWrites (what the session
changed in the vault, as label:change; these outlive the session).
attestedCheckout(opts)
Hand a card checkout to the attested enclave. The enclave reveals the card
number only inside itself and types it in its own browser.
string
required
A card number token, for example
av_card_1_number_u7x2k. Any other token
kind, including that card’s CVV token, is refused with token_not_found.object
required
A self-contained, declarative plan. The enclave starts from a blank page, so a
cart or sign-in built up in your own browser does not carry over.
number
The order total, for the owner’s approval threshold.
string
The total’s ISO currency code.
{ last4, transactionId? }.
Plan steps are goto, click, fill, fillSecret, waitFor and wait.
screenshot is refused for SDK callers. fillSecret must be followed by
exactly one state-changing step, a click on the merchant’s pay button.
Fewer, and the card is typed into a form that is never submitted. More, and one
approval would authorize two submissions. Only wait and waitFor may follow
the click. Every navigation before fillSecret must target the same merchant.
Know these limits before you use it. Each case needs the ordinary in-proxy fill
instead:
Errors:
invalid_plan, and the attested codes on
Error codes. A verification-only key is
refused with scope_denied.
This path is off by default on most deployments. See
Attested detokenization for its status.
SdkBrowserContext
SdkPage
SdkPage has an id getter but no url() getter. Pass the URL you navigated
to when a helper needs it, for example recipesForUrl(productUrl).
Navigation and waiting
NavigationResult
string
Usually where the navigation landed, reduced to scheme, host and path. The
proxy strips the query, the fragment and any userinfo, and cuts back a path
that carries a hosted checkout’s session. It can differ from the URL you
passed, because merchants redirect.Two cases return the
url you passed, unchanged, with its query and
fragment intact:- Any token has been filled in this session. From then on the proxy does not report the landing address at all, because a page can write its own address.
- The proxy is older than this contract and sent no
sameDocument.
boolean | undefined
The browser’s own answer to whether the document stayed.
true for a fragment
jump or a History API route change, where the form and what was typed into it
are untouched. false when the document was replaced. undefined from a proxy
too old to say.insecure_site from goto never committed anything: the tab is still on the
page it was on. Do not treat it as a navigation that may have landed, and do not
retry the same address. See insecure_site.
When a goto times out after the page committed a new document, the error carries
navigation: { committed: true, status? }: the tab is on the new document, and whatever was typed into the
old one is gone. A status of 403 or 429 there is a block wall, not a slow
checkout.
Filling and interacting
Fill options
fill, type, pressSequentially and selectOption take:
number
The checkout total this fill commits to. Pass it on the call that fills the
card number. At or above the card’s approval threshold, the proxy holds
and asks the owner before any real value is typed. Ignored for non-card
values.
string
The total’s ISO code, for example
GBP. Always pass the real code for a
non-USD checkout. Limits and approval thresholds are in US dollars, so a
foreign total is converted at a daily reference rate, plus a 3% safety buffer
for card network fees, and then checked normally. A currency with no rate, or
a rate older than 48 hours, cannot be converted: the total is treated as
unknown, and the fill is held for approval or refused as
checkout_total_unknown. Omit it (or pass USD) for dollar checkouts.number
default:"30000"
Bounds the browser work: waiting for the field to become actionable, and the
action. It does not count time spent waiting on the owner (an approval or a
live CVV entry).
0 means no timeout at all. Pass one whenever a miss is a
possible outcome rather than a fault.FillReport
fill, type and pressSequentially, on pages, locators and frame elements,
resolve to a FillReport:
{ verification: true, card_fields_verified: true }. Nothing is
reserved, no spending limit is checked and the owner is not alerted for that
fill. Every other
fill reports {}. Use it to tell a probe of a checkout’s card fields from a
purchase.
Select choices
A choice that names no option, or more than one, is refused withinvalid_choice before anything is sent. So is an empty one: "", or a label
that is blank once invisible characters and white space are trimmed, which would
select the placeholder option. { value } and { index } need a proxy that
supports them. Against an older proxy they are refused locally with
invalid_choice, because that proxy would select the placeholder and report
success. A plain string still works there.
ActionResult
boolean
The action opened a new tab. Switch with
waitForPopup(). Absent is
inconclusive: it means no tab arrived within the short window the proxy
watches after the action. A tab the site opens later, for example a 3-D
Secure or wallet window after a server round trip, is still queued for
waitForPopup() with nothing reporting it. Before you treat the opener as the
result of a submit, call hasPendingPopup(), and call waitForPopup() when
the checkout plainly moved to another tab.boolean | 'unsettled' | undefined
Whether a card number this page filled is still in its form after the action.
false means the action took the tab off that checkout or edited the field.
"unsettled" means a navigation had not settled; the next click or press
reports the settled state. undefined (the field is absent) means the proxy
did not report it, for example an older proxy. It does not mean false:
treat it as unknown, and neither refill the card nor discard the form because
of it.Looking at the page
snapshot(options?)
The accessibility tree as YAML, reaching into iframes including cross-origin
ones.
number
default:"12"
How deep to walk. The proxy clamps it to 30.
number
Line offset for the next page of a truncated capture. Pass it with the depth
the truncation note names: a cursor is an offset into a capture at that
depth.
cursor and depth for the next page.
A [ref=eN] handle is a selector: aria-ref=e7 works for a count, a wait, a
click or a press. A fill refuses a ref, because an in-frame ref reaches into
an iframe from the top-level page and would bypass the check on that frame’s
origin. Field values are never in a snapshot.
screenshot(options?)
Options: path, timeout (default 20000), fullPage. Returns a Uint8Array (a Buffer
at runtime) carrying redaction and reason as properties. redaction is
none, fields (substituted fields boxed out), blackout or unknown.
unknown means the proxy sent no redaction metadata, which an older proxy than
your SDK does. Treat it as possibly sensitive and possibly blacked out, never as
none. Only none tells you the pixels are the untouched page.
path, the SDK also writes the PNG to that file. It checks the path
before the capture, and these failures are not SelfAgentPayError:
In the last case, read
err.screenshot for the image and its redaction,
reason and securityCodeAuthorizationReleased, which the proxy reports only
once.
Once any token is filled, the image is a solid black rectangle for the rest of
the session, across navigation, a new tab and page.close(). Only a new proxy
session clears it.
A capture between a card number and its security code leaves the pairing in
place. A capture that cannot complete fails rather than disturbing the page: the
number is still in the form and the security code is still the next fill, so do
not refill the number. If a capture ever comes back carrying
securityCodeAuthorizationReleased, fill the number again and then the code with
nothing in between.
observe()
Returns a PageObservation: passwordField, identifierField,
contactField, codeSent, error, rateLimited, a value-free reason, and
errorText, the page’s own validation messages (scrubbed of filled values; read
it whenever it is non-empty). The fields match the MCP
observe_page tool.
“On screen” means what the proxy can establish without running script in the
page: a laid-out box, not display: none or visibility: hidden, and
horizontally on the canvas. A control kept mounted at opacity: 0 still reads as
present.
detectChallenge()
Returns { detected, type?, types, reason?, signature?, deliveredCode?, ambiguousOnly?, inconclusive? }.
type and reason are absent when nothing was detected.
type is "three_ds", "otp", "captcha" or "blocked". The first three
rank 3-D Secure above OTP above CAPTCHA, with one exception: a weak otp
carrying ambiguousOnly ranks below captcha. types lists every pattern
matched; a weak ambiguous otp is not probed once a stronger challenge is
established, so it can be absent.
blocked is a silent block: the site stopped answering without saying why. It
is reported only when a finished scan found no announced challenge, and then
types is ["blocked"]. signature names the shape it took:
Hand a
blocked page to the owner like a CAPTCHA, with
requestChallengeHandoff(). Do not retry, reload or search again to get past
it. The owner either gets through or confirms the site refused.
deliveredCode: true appears on an otp when the page says the code was sent
by SMS, email or another channel. No stored authenticator secret can produce
that code, so never fill tokens.totp for it: hand off instead. The flag is
absent when the page named no channel.
See Handling challenges.
Handing off
requestChallengeHandoff()
Ask the account owner with a dashboard banner, plus an email or alert on each
channel they turned on while their A checkout needs me to clear a bank or
verification step switch is on, with a link that streams this live page. Blocks until they finish, the wait times out (about 10
minutes), or the session ends.
Returns { outcome, challengeType, durationMs }. outcome is solved,
completed, released_unsolved, failed or abandoned. Carry on after
solved. After completed, look at the page first. After released_unsolved,
detect again and hand off again. Stop after failed, abandoned or anything
you do not recognise.
While it is parked, every other page call is rejected with challenge_required,
so you cannot watch the owner type. Afterwards, take no screenshots: in a session
with no token fill they are not blacked out, and the page can echo what the owner
typed.
Errors: challenge_required when the handoff ended without the owner
finishing: the wait timed out, a newer handoff superseded this one, or it ended
another way. Request a new handoff only after a timeout, never after a
supersede. Also handoff_not_applicable (a detection carrying
ambiguousOnly; the page stays live and yours), challenge_scan_incomplete
(the proxy’s own re-check could not finish; detect again and ask again), and
unknown_target, which has two variants:
Hints
hint(kind, target, context?)
Tell the vault where something is on this page. target takes selector,
frame, read and attribute; context takes login for
login.totp_secret. selector and frame are each at most 512 characters.
Returns { kind, accepted, checked }, plus what the vault read:
After any token fill in the session, an
order.total hint answers { accepted: true, checked: false } with no resolved, amount or untrusted. That holds only for selectors built from ids, classes, element names and presence-only attribute tests such as [data-total]. A selector or frame that compares an attribute value, such as [data-testid="total"], is refused with invalid_hint after a fill, and one declared before the fill is skipped when the card fill reads the total.
An order.total hint matching more than 8 elements, or an element over 4,000
characters, adds nothing (accepted: false). With frame, the hint also reads
the frame’s whole text and every other frame on the page. Over 20,000
characters in the frame, over 16,384 counting the page’s own text, or more than
32 frames on the page, the hint is dropped: accepted: false even though the selector matched (after a token
fill, { accepted: true, checked: false }, and it is dropped at the card fill).
The total then stays unknown. Point at a total outside the frame if there is
one. Otherwise a card with an approval threshold asks the account holder, and
a card with spending limits is refused with checkout_total_unknown. See
Totals inside an iframe.
invalid_hint for a malformed hint, a selector outside the
plain CSS grammar hints accept, a selector or frame over 512 characters, an attribute value comparison declared after
a token fill, or a fifth distinct hint of the same kind on one page (the limit
is 4 per kind, per page; repeating a hint does not count, and a navigation
clears the count), and hint_unresolved when a storing hint found
nothing usable where you pointed. Fix what they name and retry. hint_uncertain
means a storing hint’s write may have committed and only its answer was lost.
Do not retry it: enrollment never overwrites a stored secret, so a retry
is refused with hint_unresolved and reads like a second failure. Carry on,
and tell the owner to check the login on their Logins page.
Full guide: Page hints.
hintKinds()
Returns the kinds this deployment accepts, as
{ kind, purpose, when, reads, attributes, allowAfterFill }[]. reads lists
the read modes (text, attribute), attributes the attribute names
read: "attribute" may name for that kind, and allowAfterFill: false means
the reply stops reporting what was read once a token has been filled. Kinds are
added server side, so ask rather than assume.
Recording
recordOutcome(options?)
Record that this session reached a confirmed checkout. This is the only call
that sets confirmationDetected. Optionally pass orderTotal, a display string
such as "$42.00". It is kept only when it is at most 24 characters and carries
a currency sign, a currency code or two decimal places. Anything else, a bare
number like "1234" included, is silently left out. A whole total of exactly
three or four digits is also dropped even with a marker ("$737", "USD 1234"),
because it cannot be told apart from a security code: write it as "$737.00"
or "USD 1,234". It checks nothing on the page, so call it only once your own
check shows the order landed.
Returns the updated SessionOutcome, the same shape as outcome(). A
session that filled any token is recorded with no receipt image, because a
confirmation page can echo a filled value. That is expected, not a failure.
Errors: popup_pending when a live tab this page opened is still queued and
no waitForPopup() has taken it, because the 3-D Secure or wallet step may be
running there.
SdkLocator
SdkFrameLocator and SdkFrameElement
page.frameLocator(selector).locator(inner) returns an SdkFrameElement inside
an iframe. It has fill, type, pressSequentially (each returning a
FillReport), click, press, count, counts and waitFor, with the same
options as on SdkLocator. It has no selectOption, first or nth. This is
the supported way to target a hosted
payment frame, because listFrames() is refused once a token has been filled.
Input names vary by integration, so probe with
count() and fall back rather
than blocking for 30 seconds.
Signup and login helpers
These call the HTTP API, not the proxy, and need a key the owner has allowed to create logins. Without it they refuse withsignup_not_enabled. Each takes
optional apiKey and baseUrl. The agent signup guide
walks through the flow.
createLogin(opts)
string
required
The site’s registrable domain, for example
doordash.com. Locked to that
domain and its subdomains.string
required
A mailbox the agent controls. The owner sees it before approving.
string
Only a handle the site has already accepted. Approval stores it.
PasswordRules
The site’s stated password rules. The password is 12 to 64 characters, 24 by
default. A
maxLength below 12 is refused with password_too_short. A
minLength above 64, or greater than maxLength, is refused with
invalid_policy: on a site that needs more than 64, the account holder adds
the login. symbols filters the vault’s safe set, !@#$*+-_=.?, and can
never widen it. A symbols with none of those characters is refused with
invalid_policy. The password always holds a lowercase letter, an uppercase
letter and a digit, plus a symbol unless noSymbols is set.{ requestId, status, expiresAt }. status is "pending", or
"approved" when the owner has pre-approved signups for this site with this
exact email. Pre-approval completes at once for at most 2 signups per listed
site and, by default, 60 per account in a 24-hour window. Past either ceiling, or
when another approval check fails, the request comes back "pending" instead.
See Limits. The 500-login ceiling is the
exception: an account that already holds 500 logins gets login_limit straight
away, pre-approved site or not, and no request is created.
An approved handle still carries no tokens: poll requestId right
away and the first answer has them. A pending result is a stop for
createLogin, not a retry point.
Errors: signup_not_enabled, too_many_pending, login_limit (the
account already holds 500 logins, counting ones the account holder added; no
request is created), invalid_website, invalid_email, invalid_username,
invalid_policy, password_too_short, rate_limited, write_outcome_unknown.
rate_limited means the key’s shared budget of 20 credential writes per minute
is spent. createLogin, setLoginUsername and rotateLoginPassword all draw on
it. Wait retryAfterSeconds, then call again. too_many_pending is also a 429,
but it clears only when the owner decides a waiting request, so it carries no
retryAfterSeconds.
getSignupRequest(opts)
Read one request by requestId. Optional timeoutMs bounds this one poll:
default 30000, at most 30000, and never under 1000.
Returns { status: "pending", expiresAt },
{ status: "denied" }, { status: "expired" }, or
{ status: "approved", login, passwordPolicy, passwordOwnerManaged? }.
login is the new login’s catalog entry, so its
label is login.label and its tokens are under login.tokens, with
login.hasEmail and login.hasUsername saying which identifier it carries.
passwordPolicy describes the password the vault generated. passwordOwnerManaged
is true when the account holder replaced that password from their dashboard
before you polled: the policy then describes a password that no longer exists,
the token still fills the account holder’s password, and rotateLoginPassword
is refused for this login for good. If the site rejects the password, tell the
account holder. Absent or false means the generated password is still in place.
Errors: request_not_found, scope_denied.
awaitSignupApproval(opts)
Poll until the owner decides. Takes requestId, plus timeoutMs (default 15
minutes) and intervalMs (default 5000, never faster than 2000). A 429’s
Retry-After is honoured before the next poll, within the same deadline.
Returns the approved { login, passwordPolicy, passwordOwnerManaged? }.
A failing poll that another tick can get past (a dropped connection, a 408, 429
or 5xx) is absorbed and the wait carries on. A refusal that says the caller is
wrong (request_not_found, any other 4xx, a missing key) is thrown at once.
Errors: signup_denied, signup_expired, approval_timeout (the wait
ended; the request is still live), and, thrown at once, request_not_found and
scope_denied (the owner removed the approved login from this key’s access).
setLoginUsername(opts)
Record a handle the site has already accepted. Takes label and username.
Returns { login }. An exact replay of the same handle is idempotent.
Only the API key that created the login can set its username, and only within
seven days of the login being created. Seven days is the default; a deployment
can set a different window. Any other key, or a later call, gets not_found,
the same answer as an unknown label. An exact replay of a handle already stored
still succeeds after the seven days.
Errors: username_already_set, not_found, invalid_username,
daily_limit, rate_limited (with retryAfterSeconds; an exact replay of a
handle already stored is not charged), write_outcome_unknown (re-send the same
handle).
rotateLoginPassword(opts)
Replace the generated password on a login an agent created, when the site
rejected it. Takes label plus the password rules, with the same bounds as
createLogin: a minLength above 64 or a symbols outside the safe set is
invalid_policy, and a maxLength below 12 is password_too_short. The mock
tokens do not change. Never use it on a form that asks for the current password.
Only the API key that created the login can rotate its password, only within
seven days of the login being created, and only while the account holder has not
replaced the password. Seven days is the default; a deployment can set a
different window. Any other key, or a later call, gets not_rotatable, the same
answer as an unknown label.
Returns { login, passwordPolicy, rotated: true }: the login’s catalog
entry with the same tokens, and the rules the new password was generated under.
Errors: not_rotatable, not_found, invalid_policy,
password_too_short, daily_limit, rate_limited (with retryAfterSeconds),
write_outcome_unknown (stop and hand it to the owner).
Registry helpers
recipesForUrl strips the URL because the lookup is unauthenticated and can
reach logs: for an absolute URL such as https://shop.example.com/cart, the
query, fragment and any hosted-checkout token (a Stripe cs_... session, a
Shopify checkout token) never leave your process. Stripping needs a URL that
parses. A relative or malformed string, such as shop.example.com/cart, is sent
to the registry exactly as you passed it, so never pass one that contains a
session token.
recipesForUrl and buyableMerchants set no code on failure: a non-2xx
reports the status in its message. Branch on which call threw. A malformed
successful body, or an abort mid-body, escapes as a native error, not a
SelfAgentPayError (see Overview).
submitRecipe and recipeSubmission code from the response body:
invalid_recipe (with validationErrors), rate_limited (the budget of 20
in any 24 hours, with no retryAfterSeconds), not_found and unauthorized.
Utilities
SelfAgentPayError
requestTimeoutMs(method, params?)
Returns the deadline in milliseconds the SDK applies to one call. See
Sessions and timeouts.
sdkCapabilities
Markers for features that live inside existing methods, so they cannot be
detected by checking for a method: fillTimeout (fill options carry timeout
to the proxy) and locatorCounts (locators answer counts()). Absent means an
older build.
Related
Sessions and timeouts
Deadlines, and which errors end a session.
Error codes
Every code, and whether retrying is safe.