[{"data":1,"prerenderedAt":91},["ShallowReactive",2],{"docs:core-concepts/users":3},{"title":4,"navTitle":4,"description":5,"api":6,"html":7,"toc":8,"path":80,"tab":81,"group":82,"prev":83,"next":86,"markdownUrl":89,"sourceUrl":90,"lastModified":6},"Users","Run endpoints for one user and push live events to that user",null,"\u003Ch2 id=\"what-is-the-user-scope\">\u003Ca class=\"docs-anchor\" href=\"#what-is-the-user-scope\" aria-label=\"Link to this section\">\u003C/a>What is the user scope?\u003C/h2>\n\u003Cp>A thread belongs to one conversation. The user scope belongs to one user and spans every thread that user owns. It has five parts:\u003C/p>\n\u003Cul>\n\u003Cli>\u003Cstrong>User endpoints\u003C/strong>: HTTP routes that run for one user\u003C/li>\n\u003Cli>\u003Cstrong>User values\u003C/strong>: durable JSON values for one user\u003C/li>\n\u003Cli>\u003Cstrong>User event stream\u003C/strong>: a WebSocket that delivers live events to the user's connected clients\u003C/li>\n\u003Cli>\u003Cstrong>User room\u003C/strong>: an optional WebSocket hub that receives frames from the user's clients\u003C/li>\n\u003Cli>\u003Cstrong>Credential grants\u003C/strong>: credentials that the runtime adds to requests for the user\u003C/li>\n\u003C/ul>\n\u003Cp>Use the user scope for data that more than one thread reads: preferences, quotas, counters, and registries.\u003C/p>\n\u003Ch2 id=\"user-endpoints\">\u003Ca class=\"docs-anchor\" href=\"#user-endpoints\" aria-label=\"Link to this section\">\u003C/a>User endpoints\u003C/h2>\n\u003Ch3 id=\"quick-start\">\u003Ca class=\"docs-anchor\" href=\"#quick-start\" aria-label=\"Link to this section\">\u003C/a>Quick start\u003C/h3>\n\u003Cp>Create a file in \u003Ccode>agents/users/\u003C/code> and export a \u003Ccode>defineUserEndpoint\u003C/code> call as the default export:\u003C/p>\n\u003Cfigure class=\"docs-code\" data-docs=\"code\" data-lang=\"typescript\">\u003Cpre class=\"shiki shiki-themes one-light github-dark\" style=\"--shiki-light:#383A42;--shiki-dark:#e1e4e8;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#24292e\" tabindex=\"0\">\u003Ccode>\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#6A737D;--shiki-dark-font-style:inherit\">// agents/users/preferences.ts\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#A626A4;--shiki-dark:#F97583\">import\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\"> { \u003C/span>\u003Cspan style=\"--shiki-light:#E45649;--shiki-dark:#E1E4E8\">defineUserEndpoint\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\"> } \u003C/span>\u003Cspan style=\"--shiki-light:#A626A4;--shiki-dark:#F97583\">from\u003C/span>\u003Cspan style=\"--shiki-light:#50A14F;--shiki-dark:#9ECBFF\"> '@standardagents/spec'\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">;\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#A626A4;--shiki-dark:#F97583\">export\u003C/span>\u003Cspan style=\"--shiki-light:#E45649;--shiki-dark:#F97583\"> default\u003C/span>\u003Cspan style=\"--shiki-light:#4078F2;--shiki-dark:#B392F0\"> defineUserEndpoint\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">(\u003C/span>\u003Cspan style=\"--shiki-light:#A626A4;--shiki-dark:#F97583\">async\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\"> (\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#FFAB70\">_req\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">, \u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#FFAB70\">user\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">) \u003C/span>\u003Cspan style=\"--shiki-light:#A626A4;--shiki-dark:#F97583\">=>\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\"> {\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#A626A4;--shiki-dark:#F97583\">  const\u003C/span>\u003Cspan style=\"--shiki-light:#986801;--shiki-dark:#79B8FF\"> theme\u003C/span>\u003Cspan style=\"--shiki-light:#0184BC;--shiki-dark:#F97583\"> =\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\"> (\u003C/span>\u003Cspan style=\"--shiki-light:#A626A4;--shiki-dark:#F97583\">await\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\"> user.\u003C/span>\u003Cspan style=\"--shiki-light:#4078F2;--shiki-dark:#B392F0\">getValue\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">&#x3C;\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#79B8FF\">string\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">>(\u003C/span>\u003Cspan style=\"--shiki-light:#50A14F;--shiki-dark:#9ECBFF\">'preferences.theme'\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">)) \u003C/span>\u003Cspan style=\"--shiki-light:#0184BC;--shiki-dark:#F97583\">??\u003C/span>\u003Cspan style=\"--shiki-light:#50A14F;--shiki-dark:#9ECBFF\"> 'light'\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">;\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#A626A4;--shiki-dark:#F97583\">  return\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\"> Response.\u003C/span>\u003Cspan style=\"--shiki-light:#4078F2;--shiki-dark:#B392F0\">json\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">({ \u003C/span>\u003Cspan style=\"--shiki-light:#E45649;--shiki-dark:#E1E4E8\">userId\u003C/span>\u003Cspan style=\"--shiki-light:#0184BC;--shiki-dark:#E1E4E8\">:\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\"> user.\u003C/span>\u003Cspan style=\"--shiki-light:#E45649;--shiki-dark:#E1E4E8\">userId\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">, \u003C/span>\u003Cspan style=\"--shiki-light:#E45649;--shiki-dark:#E1E4E8\">theme\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\"> });\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">});\u003C/span>\u003C/span>\u003C/code>\u003C/pre>\u003C/figure>\n\u003Cp>The endpoint is available at \u003Ccode>GET /api/users/me/preferences\u003C/code>. Standard Agents Builder discovers it from the \u003Ccode>agents/users/\u003C/code> directory.\u003C/p>\n\u003Ch3 id=\"file-based-routing\">\u003Ca class=\"docs-anchor\" href=\"#file-based-routing\" aria-label=\"Link to this section\">\u003C/a>File-based routing\u003C/h3>\n\u003Cp>Routing follows the rules of \u003Ca href=\"/docs/core-concepts/api#file-based-routing\">thread endpoints\u003C/a>. The path sets the route, a suffix sets the method, and \u003Ccode>[name]\u003C/code> and \u003Ccode>[*]\u003C/code> capture path segments.\u003C/p>\n\u003Cdiv class=\"docs-table\">\u003Ctable>\n\u003Cthead>\n\u003Ctr>\n\u003Cth>File\u003C/th>\n\u003Cth>HTTP method\u003C/th>\n\u003Cth>Route\u003C/th>\n\u003C/tr>\n\u003C/thead>\n\u003Ctbody>\n\u003Ctr>\n\u003Ctd>\u003Ccode>preferences.ts\u003C/code>\u003C/td>\n\u003Ctd>GET\u003C/td>\n\u003Ctd>\u003Ccode>/api/users/me/preferences\u003C/code>\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>preferences.put.ts\u003C/code>\u003C/td>\n\u003Ctd>PUT\u003C/td>\n\u003Ctd>\u003Ccode>/api/users/me/preferences\u003C/code>\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>quota/spend.post.ts\u003C/code>\u003C/td>\n\u003Ctd>POST\u003C/td>\n\u003Ctd>\u003Ccode>/api/users/me/quota/spend\u003C/code>\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>items/[id].ts\u003C/code>\u003C/td>\n\u003Ctd>GET\u003C/td>\n\u003Ctd>\u003Ccode>/api/users/me/items/:id\u003C/code>\u003C/td>\n\u003C/tr>\n\u003C/tbody>\n\u003C/table>\u003C/div>\n\u003Cp>\u003Ccode>me\u003C/code> is the authenticated caller. Every route under \u003Ccode>/api/users/me/\u003C/code> requires authentication.\nA super admin session has no user account, so a \u003Ccode>me\u003C/code> route returns HTTP 400 for it.\u003C/p>\n\u003Cp>The paths \u003Ccode>env\u003C/code>, \u003Ccode>kv\u003C/code>, \u003Ccode>hub\u003C/code>, and \u003Ccode>stream\u003C/code> directly under \u003Ccode>/api/users/me/\u003C/code> belong to built-in routes. Do not name a user endpoint file after one of them.\u003C/p>\n\u003Cp>A request that matches no user endpoint returns HTTP 404.\u003C/p>\n\u003Ch3 id=\"handler-function\">\u003Ca class=\"docs-anchor\" href=\"#handler-function\" aria-label=\"Link to this section\">\u003C/a>Handler function\u003C/h3>\n\u003Cfigure class=\"docs-code\" data-docs=\"code\" data-lang=\"typescript\">\u003Cpre class=\"shiki shiki-themes one-light github-dark\" style=\"--shiki-light:#383A42;--shiki-dark:#e1e4e8;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#24292e\" tabindex=\"0\">\u003Ccode>\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#A626A4;--shiki-dark:#F97583\">declare\u003C/span>\u003Cspan style=\"--shiki-light:#A626A4;--shiki-dark:#F97583\"> function\u003C/span>\u003Cspan style=\"--shiki-light:#4078F2;--shiki-dark:#B392F0\"> defineUserEndpoint\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">(\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#4078F2;--shiki-dark:#B392F0\">  handler\u003C/span>\u003Cspan style=\"--shiki-light:#0184BC;--shiki-dark:#F97583\">:\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\"> (\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#FFAB70\">    req\u003C/span>\u003Cspan style=\"--shiki-light:#0184BC;--shiki-dark:#F97583\">:\u003C/span>\u003Cspan style=\"--shiki-light:#C18401;--shiki-dark:#B392F0\"> Request\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">,\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#FFAB70\">    user\u003C/span>\u003Cspan style=\"--shiki-light:#0184BC;--shiki-dark:#F97583\">:\u003C/span>\u003Cspan style=\"--shiki-light:#C18401;--shiki-dark:#B392F0\"> UserState\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">,\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#FFAB70\">    params\u003C/span>\u003Cspan style=\"--shiki-light:#0184BC;--shiki-dark:#F97583\">:\u003C/span>\u003Cspan style=\"--shiki-light:#C18401;--shiki-dark:#B392F0\"> Record\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">&#x3C;\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#79B8FF\">string\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">, \u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#79B8FF\">string\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">>\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">  ) \u003C/span>\u003Cspan style=\"--shiki-light:#A626A4;--shiki-dark:#F97583\">=>\u003C/span>\u003Cspan style=\"--shiki-light:#C18401;--shiki-dark:#B392F0\"> Response\u003C/span>\u003Cspan style=\"--shiki-light:#0184BC;--shiki-dark:#F97583\"> |\u003C/span>\u003Cspan style=\"--shiki-light:#C18401;--shiki-dark:#B392F0\"> Promise\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">&#x3C;\u003C/span>\u003Cspan style=\"--shiki-light:#C18401;--shiki-dark:#B392F0\">Response\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">>\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">)\u003C/span>\u003Cspan style=\"--shiki-light:#0184BC;--shiki-dark:#F97583\">:\u003C/span>\u003Cspan style=\"--shiki-light:#C18401;--shiki-dark:#B392F0\"> MarkedUserEndpoint\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">;\u003C/span>\u003C/span>\u003C/code>\u003C/pre>\u003C/figure>\n\u003Cul>\n\u003Cli>\u003Ccode>req\u003C/code>: the Web API \u003Ccode>Request\u003C/code>\u003C/li>\n\u003Cli>\u003Ccode>user\u003C/code>: the \u003Ccode>UserState\u003C/code> of the addressed user\u003C/li>\n\u003Cli>\u003Ccode>params\u003C/code>: the values captured from \u003Ccode>[name]\u003C/code> and \u003Ccode>[*]\u003C/code> path segments\u003C/li>\n\u003C/ul>\n\u003Cp>A user endpoint receives no \u003Ccode>ThreadState\u003C/code>. An exception that the handler does not catch returns HTTP 500.\u003C/p>\n\u003Ch3 id=\"one-run-at-a-time-for-each-user\">\u003Ca class=\"docs-anchor\" href=\"#one-run-at-a-time-for-each-user\" aria-label=\"Link to this section\">\u003C/a>One run at a time for each user\u003C/h3>\n\u003Cp>For one user, Standard Agents Builder runs one user endpoint at a time. Each run sees every write that earlier runs completed. A user endpoint can therefore read a value, check a limit, and write the new value without a race:\u003C/p>\n\u003Cfigure class=\"docs-code\" data-docs=\"code\" data-lang=\"typescript\">\u003Cpre class=\"shiki shiki-themes one-light github-dark\" style=\"--shiki-light:#383A42;--shiki-dark:#e1e4e8;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#24292e\" tabindex=\"0\">\u003Ccode>\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#6A737D;--shiki-dark-font-style:inherit\">// agents/users/quota/spend.post.ts\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#A626A4;--shiki-dark:#F97583\">import\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\"> { \u003C/span>\u003Cspan style=\"--shiki-light:#E45649;--shiki-dark:#E1E4E8\">defineUserEndpoint\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\"> } \u003C/span>\u003Cspan style=\"--shiki-light:#A626A4;--shiki-dark:#F97583\">from\u003C/span>\u003Cspan style=\"--shiki-light:#50A14F;--shiki-dark:#9ECBFF\"> '@standardagents/spec'\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">;\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#A626A4;--shiki-dark:#F97583\">import\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\"> { \u003C/span>\u003Cspan style=\"--shiki-light:#E45649;--shiki-dark:#E1E4E8\">z\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\"> } \u003C/span>\u003Cspan style=\"--shiki-light:#A626A4;--shiki-dark:#F97583\">from\u003C/span>\u003Cspan style=\"--shiki-light:#50A14F;--shiki-dark:#9ECBFF\"> 'zod'\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">;\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#A626A4;--shiki-dark:#F97583\">const\u003C/span>\u003Cspan style=\"--shiki-light:#986801;--shiki-dark:#79B8FF\"> SpendInput\u003C/span>\u003Cspan style=\"--shiki-light:#0184BC;--shiki-dark:#F97583\"> =\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\"> z.\u003C/span>\u003Cspan style=\"--shiki-light:#4078F2;--shiki-dark:#B392F0\">object\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">({\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#E45649;--shiki-dark:#E1E4E8\">  amount\u003C/span>\u003Cspan style=\"--shiki-light:#0184BC;--shiki-dark:#E1E4E8\">:\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\"> z.\u003C/span>\u003Cspan style=\"--shiki-light:#4078F2;--shiki-dark:#B392F0\">number\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">().\u003C/span>\u003Cspan style=\"--shiki-light:#4078F2;--shiki-dark:#B392F0\">int\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">().\u003C/span>\u003Cspan style=\"--shiki-light:#4078F2;--shiki-dark:#B392F0\">positive\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">(),\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">});\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#A626A4;--shiki-dark:#F97583\">const\u003C/span>\u003Cspan style=\"--shiki-light:#986801;--shiki-dark:#79B8FF\"> DAILY_LIMIT\u003C/span>\u003Cspan style=\"--shiki-light:#0184BC;--shiki-dark:#F97583\"> =\u003C/span>\u003Cspan style=\"--shiki-light:#986801;--shiki-dark:#79B8FF\"> 100\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">;\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#A626A4;--shiki-dark:#F97583\">export\u003C/span>\u003Cspan style=\"--shiki-light:#E45649;--shiki-dark:#F97583\"> default\u003C/span>\u003Cspan style=\"--shiki-light:#4078F2;--shiki-dark:#B392F0\"> defineUserEndpoint\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">(\u003C/span>\u003Cspan style=\"--shiki-light:#A626A4;--shiki-dark:#F97583\">async\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\"> (\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#FFAB70\">req\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">, \u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#FFAB70\">user\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">) \u003C/span>\u003Cspan style=\"--shiki-light:#A626A4;--shiki-dark:#F97583\">=>\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\"> {\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#A626A4;--shiki-dark:#F97583\">  const\u003C/span>\u003Cspan style=\"--shiki-light:#986801;--shiki-dark:#79B8FF\"> input\u003C/span>\u003Cspan style=\"--shiki-light:#0184BC;--shiki-dark:#F97583\"> =\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\"> SpendInput.\u003C/span>\u003Cspan style=\"--shiki-light:#4078F2;--shiki-dark:#B392F0\">safeParse\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">(\u003C/span>\u003Cspan style=\"--shiki-light:#A626A4;--shiki-dark:#F97583\">await\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\"> req.\u003C/span>\u003Cspan style=\"--shiki-light:#4078F2;--shiki-dark:#B392F0\">json\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">().\u003C/span>\u003Cspan style=\"--shiki-light:#4078F2;--shiki-dark:#B392F0\">catch\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">(() \u003C/span>\u003Cspan style=\"--shiki-light:#A626A4;--shiki-dark:#F97583\">=>\u003C/span>\u003Cspan style=\"--shiki-light:#986801;--shiki-dark:#79B8FF\"> null\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">));\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#A626A4;--shiki-dark:#F97583\">  if\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\"> (\u003C/span>\u003Cspan style=\"--shiki-light:#0184BC;--shiki-dark:#F97583\">!\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">input.\u003C/span>\u003Cspan style=\"--shiki-light:#E45649;--shiki-dark:#E1E4E8\">success\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">) {\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#A626A4;--shiki-dark:#F97583\">    return\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\"> Response.\u003C/span>\u003Cspan style=\"--shiki-light:#4078F2;--shiki-dark:#B392F0\">json\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">({ \u003C/span>\u003Cspan style=\"--shiki-light:#E45649;--shiki-dark:#E1E4E8\">error\u003C/span>\u003Cspan style=\"--shiki-light:#0184BC;--shiki-dark:#E1E4E8\">:\u003C/span>\u003Cspan style=\"--shiki-light:#50A14F;--shiki-dark:#9ECBFF\"> 'Send a positive integer \"amount\".'\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\"> }, { \u003C/span>\u003Cspan style=\"--shiki-light:#E45649;--shiki-dark:#E1E4E8\">status\u003C/span>\u003Cspan style=\"--shiki-light:#0184BC;--shiki-dark:#E1E4E8\">:\u003C/span>\u003Cspan style=\"--shiki-light:#986801;--shiki-dark:#79B8FF\"> 400\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\"> });\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">  }\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#A626A4;--shiki-dark:#F97583\">  const\u003C/span>\u003Cspan style=\"--shiki-light:#986801;--shiki-dark:#79B8FF\"> key\u003C/span>\u003Cspan style=\"--shiki-light:#0184BC;--shiki-dark:#F97583\"> =\u003C/span>\u003Cspan style=\"--shiki-light:#50A14F;--shiki-dark:#9ECBFF\"> `quota.\u003C/span>\u003Cspan style=\"--shiki-light:#CA1243;--shiki-dark:#9ECBFF\">${\u003C/span>\u003Cspan style=\"--shiki-light:#A626A4;--shiki-dark:#F97583\">new\u003C/span>\u003Cspan style=\"--shiki-light:#4078F2;--shiki-dark:#B392F0\"> Date\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#9ECBFF\">()\u003C/span>\u003Cspan style=\"--shiki-light:#50A14F;--shiki-dark:#9ECBFF\">.\u003C/span>\u003Cspan style=\"--shiki-light:#4078F2;--shiki-dark:#B392F0\">toISOString\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#9ECBFF\">()\u003C/span>\u003Cspan style=\"--shiki-light:#50A14F;--shiki-dark:#9ECBFF\">.\u003C/span>\u003Cspan style=\"--shiki-light:#4078F2;--shiki-dark:#B392F0\">slice\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#9ECBFF\">(\u003C/span>\u003Cspan style=\"--shiki-light:#986801;--shiki-dark:#79B8FF\">0\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#9ECBFF\">,\u003C/span>\u003Cspan style=\"--shiki-light:#986801;--shiki-dark:#79B8FF\"> 10\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#9ECBFF\">)\u003C/span>\u003Cspan style=\"--shiki-light:#CA1243;--shiki-dark:#9ECBFF\">}\u003C/span>\u003Cspan style=\"--shiki-light:#50A14F;--shiki-dark:#9ECBFF\">`\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">;\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#A626A4;--shiki-dark:#F97583\">  const\u003C/span>\u003Cspan style=\"--shiki-light:#986801;--shiki-dark:#79B8FF\"> used\u003C/span>\u003Cspan style=\"--shiki-light:#0184BC;--shiki-dark:#F97583\"> =\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\"> (\u003C/span>\u003Cspan style=\"--shiki-light:#A626A4;--shiki-dark:#F97583\">await\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\"> user.\u003C/span>\u003Cspan style=\"--shiki-light:#4078F2;--shiki-dark:#B392F0\">getValue\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">&#x3C;\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#79B8FF\">number\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">>(\u003C/span>\u003Cspan style=\"--shiki-light:#E45649;--shiki-dark:#E1E4E8\">key\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">)) \u003C/span>\u003Cspan style=\"--shiki-light:#0184BC;--shiki-dark:#F97583\">??\u003C/span>\u003Cspan style=\"--shiki-light:#986801;--shiki-dark:#79B8FF\"> 0\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">;\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#A626A4;--shiki-dark:#F97583\">  const\u003C/span>\u003Cspan style=\"--shiki-light:#986801;--shiki-dark:#79B8FF\"> next\u003C/span>\u003Cspan style=\"--shiki-light:#0184BC;--shiki-dark:#F97583\"> =\u003C/span>\u003Cspan style=\"--shiki-light:#E45649;--shiki-dark:#E1E4E8\"> used\u003C/span>\u003Cspan style=\"--shiki-light:#0184BC;--shiki-dark:#F97583\"> +\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\"> input.\u003C/span>\u003Cspan style=\"--shiki-light:#E45649;--shiki-dark:#E1E4E8\">data\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">.\u003C/span>\u003Cspan style=\"--shiki-light:#E45649;--shiki-dark:#E1E4E8\">amount\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">;\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#A626A4;--shiki-dark:#F97583\">  if\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\"> (\u003C/span>\u003Cspan style=\"--shiki-light:#E45649;--shiki-dark:#E1E4E8\">next\u003C/span>\u003Cspan style=\"--shiki-light:#0184BC;--shiki-dark:#F97583\"> >\u003C/span>\u003Cspan style=\"--shiki-light:#986801;--shiki-dark:#79B8FF\"> DAILY_LIMIT\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">) {\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#A626A4;--shiki-dark:#F97583\">    return\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\"> Response.\u003C/span>\u003Cspan style=\"--shiki-light:#4078F2;--shiki-dark:#B392F0\">json\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">({ \u003C/span>\u003Cspan style=\"--shiki-light:#E45649;--shiki-dark:#E1E4E8\">ok\u003C/span>\u003Cspan style=\"--shiki-light:#0184BC;--shiki-dark:#E1E4E8\">:\u003C/span>\u003Cspan style=\"--shiki-light:#986801;--shiki-dark:#79B8FF\"> false\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">, \u003C/span>\u003Cspan style=\"--shiki-light:#E45649;--shiki-dark:#E1E4E8\">used\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">, \u003C/span>\u003Cspan style=\"--shiki-light:#E45649;--shiki-dark:#E1E4E8\">limit\u003C/span>\u003Cspan style=\"--shiki-light:#0184BC;--shiki-dark:#E1E4E8\">:\u003C/span>\u003Cspan style=\"--shiki-light:#986801;--shiki-dark:#79B8FF\"> DAILY_LIMIT\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\"> }, { \u003C/span>\u003Cspan style=\"--shiki-light:#E45649;--shiki-dark:#E1E4E8\">status\u003C/span>\u003Cspan style=\"--shiki-light:#0184BC;--shiki-dark:#E1E4E8\">:\u003C/span>\u003Cspan style=\"--shiki-light:#986801;--shiki-dark:#79B8FF\"> 429\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\"> });\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">  }\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#A626A4;--shiki-dark:#F97583\">  await\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\"> user.\u003C/span>\u003Cspan style=\"--shiki-light:#4078F2;--shiki-dark:#B392F0\">setValue\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">(\u003C/span>\u003Cspan style=\"--shiki-light:#E45649;--shiki-dark:#E1E4E8\">key\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">, \u003C/span>\u003Cspan style=\"--shiki-light:#E45649;--shiki-dark:#E1E4E8\">next\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">);\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">  user.\u003C/span>\u003Cspan style=\"--shiki-light:#4078F2;--shiki-dark:#B392F0\">emit\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">(\u003C/span>\u003Cspan style=\"--shiki-light:#50A14F;--shiki-dark:#9ECBFF\">'quota_changed'\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">, { \u003C/span>\u003Cspan style=\"--shiki-light:#E45649;--shiki-dark:#E1E4E8\">used\u003C/span>\u003Cspan style=\"--shiki-light:#0184BC;--shiki-dark:#E1E4E8\">:\u003C/span>\u003Cspan style=\"--shiki-light:#E45649;--shiki-dark:#E1E4E8\"> next\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">, \u003C/span>\u003Cspan style=\"--shiki-light:#E45649;--shiki-dark:#E1E4E8\">limit\u003C/span>\u003Cspan style=\"--shiki-light:#0184BC;--shiki-dark:#E1E4E8\">:\u003C/span>\u003Cspan style=\"--shiki-light:#986801;--shiki-dark:#79B8FF\"> DAILY_LIMIT\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\"> });\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#A626A4;--shiki-dark:#F97583\">  return\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\"> Response.\u003C/span>\u003Cspan style=\"--shiki-light:#4078F2;--shiki-dark:#B392F0\">json\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">({ \u003C/span>\u003Cspan style=\"--shiki-light:#E45649;--shiki-dark:#E1E4E8\">ok\u003C/span>\u003Cspan style=\"--shiki-light:#0184BC;--shiki-dark:#E1E4E8\">:\u003C/span>\u003Cspan style=\"--shiki-light:#986801;--shiki-dark:#79B8FF\"> true\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">, \u003C/span>\u003Cspan style=\"--shiki-light:#E45649;--shiki-dark:#E1E4E8\">used\u003C/span>\u003Cspan style=\"--shiki-light:#0184BC;--shiki-dark:#E1E4E8\">:\u003C/span>\u003Cspan style=\"--shiki-light:#E45649;--shiki-dark:#E1E4E8\"> next\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">, \u003C/span>\u003Cspan style=\"--shiki-light:#E45649;--shiki-dark:#E1E4E8\">limit\u003C/span>\u003Cspan style=\"--shiki-light:#0184BC;--shiki-dark:#E1E4E8\">:\u003C/span>\u003Cspan style=\"--shiki-light:#986801;--shiki-dark:#79B8FF\"> DAILY_LIMIT\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\"> });\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">});\u003C/span>\u003C/span>\u003C/code>\u003C/pre>\u003C/figure>\n\u003Cp>This guarantee covers user endpoints and \u003Ccode>state.user().call()\u003C/code>. It does not cover direct value writes from thread code, such as \u003Ccode>(await state.user())?.setValue(...)\u003C/code>.\u003C/p>\n\u003Ch3 id=\"userstate\">\u003Ca class=\"docs-anchor\" href=\"#userstate\" aria-label=\"Link to this section\">\u003C/a>UserState\u003C/h3>\n\u003Cdiv class=\"docs-table\">\u003Ctable>\n\u003Cthead>\n\u003Ctr>\n\u003Cth>Member\u003C/th>\n\u003Cth>Description\u003C/th>\n\u003C/tr>\n\u003C/thead>\n\u003Ctbody>\n\u003Ctr>\n\u003Ctd>\u003Ccode>userId\u003C/code>\u003C/td>\n\u003Ctd>The user's ID\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>identity()\u003C/code>\u003C/td>\n\u003Ctd>The user's profile: \u003Ccode>id\u003C/code>, \u003Ccode>username\u003C/code>, \u003Ccode>email\u003C/code>, \u003Ccode>display_name\u003C/code>, \u003Ccode>avatar_url\u003C/code>, \u003Ccode>role\u003C/code>, \u003Ccode>roles\u003C/code>, and \u003Ccode>permissions\u003C/code>\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>getValue(key)\u003C/code>\u003C/td>\n\u003Ctd>Reads one durable JSON value. Returns \u003Ccode>null\u003C/code> when the key has no value\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>setValue(key, value)\u003C/code>\u003C/td>\n\u003Ctd>Writes one value. A \u003Ccode>null\u003C/code> value deletes the key\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>listValues({ prefix, limit, offset })\u003C/code>\u003C/td>\n\u003Ctd>Lists the user's values\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>env(name)\u003C/code>\u003C/td>\n\u003Ctd>Reads a variable from the user-account environment and then from the instance environment. Rejects when neither defines the name\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>instanceEnv(name)\u003C/code>\u003C/td>\n\u003Ctd>Reads a variable from the instance environment only. Returns \u003Ccode>undefined\u003C/code> when the name is not set\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>emit(event, data)\u003C/code>\u003C/td>\n\u003Ctd>Sends a live event to the user's connected clients\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>listThreads({ agentId, limit, offset })\u003C/code>\u003C/td>\n\u003Ctd>Lists the user's threads\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>thread(threadId)\u003C/code>\u003C/td>\n\u003Ctd>Returns the \u003Ccode>ThreadState\u003C/code> of one of the user's threads. Rejects for a thread that another user owns\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>fetch(grant, input, init)\u003C/code>\u003C/td>\n\u003Ctd>Sends a request with the credential of a \u003Ca href=\"/docs/core-concepts/credential-grants\">credential grant\u003C/a>\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>clients()\u003C/code>\u003C/td>\n\u003Ctd>Lists the clients connected to the \u003Ca href=\"#user-rooms\">user room\u003C/a>\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>send(clientId, frame)\u003C/code>\u003C/td>\n\u003Ctd>Sends one frame to one client of the user room. Returns \u003Ccode>false\u003C/code> when that client is not connected\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>roomAttachments\u003C/code>\u003C/td>\n\u003Ctd>Optional methods to inspect, admit, address, or close room attachments\u003C/td>\n\u003C/tr>\n\u003C/tbody>\n\u003C/table>\u003C/div>\n\u003Cp>User values are plain JSON. Do not store credentials in them. Store a credential for an external API as a \u003Ca href=\"/docs/core-concepts/credential-grants\">credential grant\u003C/a>.\u003C/p>\n\u003Ch3 id=\"calling-a-user-endpoint-from-a-thread\">\u003Ca class=\"docs-anchor\" href=\"#calling-a-user-endpoint-from-a-thread\" aria-label=\"Link to this section\">\u003C/a>Calling a user endpoint from a thread\u003C/h3>\n\u003Cp>Tools, hooks, handlers, and thread endpoints reach the thread's user through \u003Ccode>state.user()\u003C/code>. \u003Ccode>call()\u003C/code> runs a user endpoint by its path under \u003Ccode>agents/users/\u003C/code>, with the same one-at-a-time guarantee.\u003C/p>\n\u003Cfigure class=\"docs-code\" data-docs=\"code\" data-lang=\"typescript\">\u003Cpre class=\"shiki shiki-themes one-light github-dark\" style=\"--shiki-light:#383A42;--shiki-dark:#e1e4e8;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#24292e\" tabindex=\"0\">\u003Ccode>\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#6A737D;--shiki-dark-font-style:inherit\">// agents/tools/spend_quota.ts\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#A626A4;--shiki-dark:#F97583\">import\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\"> { \u003C/span>\u003Cspan style=\"--shiki-light:#E45649;--shiki-dark:#E1E4E8\">defineTool\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\"> } \u003C/span>\u003Cspan style=\"--shiki-light:#A626A4;--shiki-dark:#F97583\">from\u003C/span>\u003Cspan style=\"--shiki-light:#50A14F;--shiki-dark:#9ECBFF\"> '@standardagents/spec'\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">;\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#A626A4;--shiki-dark:#F97583\">import\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\"> { \u003C/span>\u003Cspan style=\"--shiki-light:#E45649;--shiki-dark:#E1E4E8\">z\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\"> } \u003C/span>\u003Cspan style=\"--shiki-light:#A626A4;--shiki-dark:#F97583\">from\u003C/span>\u003Cspan style=\"--shiki-light:#50A14F;--shiki-dark:#9ECBFF\"> 'zod'\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">;\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#A626A4;--shiki-dark:#F97583\">export\u003C/span>\u003Cspan style=\"--shiki-light:#E45649;--shiki-dark:#F97583\"> default\u003C/span>\u003Cspan style=\"--shiki-light:#4078F2;--shiki-dark:#B392F0\"> defineTool\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">({\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#E45649;--shiki-dark:#E1E4E8\">  description\u003C/span>\u003Cspan style=\"--shiki-light:#0184BC;--shiki-dark:#E1E4E8\">:\u003C/span>\u003Cspan style=\"--shiki-light:#50A14F;--shiki-dark:#9ECBFF\"> 'Spend units of the daily quota before metered work.'\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">,\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#E45649;--shiki-dark:#E1E4E8\">  args\u003C/span>\u003Cspan style=\"--shiki-light:#0184BC;--shiki-dark:#E1E4E8\">:\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\"> z.\u003C/span>\u003Cspan style=\"--shiki-light:#4078F2;--shiki-dark:#B392F0\">object\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">({\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#E45649;--shiki-dark:#E1E4E8\">    amount\u003C/span>\u003Cspan style=\"--shiki-light:#0184BC;--shiki-dark:#E1E4E8\">:\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\"> z.\u003C/span>\u003Cspan style=\"--shiki-light:#4078F2;--shiki-dark:#B392F0\">number\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">().\u003C/span>\u003Cspan style=\"--shiki-light:#4078F2;--shiki-dark:#B392F0\">int\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">().\u003C/span>\u003Cspan style=\"--shiki-light:#4078F2;--shiki-dark:#B392F0\">positive\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">().\u003C/span>\u003Cspan style=\"--shiki-light:#4078F2;--shiki-dark:#B392F0\">describe\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">(\u003C/span>\u003Cspan style=\"--shiki-light:#50A14F;--shiki-dark:#9ECBFF\">'Units to spend'\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">),\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">  }),\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#4078F2;--shiki-dark:#B392F0\">  execute\u003C/span>\u003Cspan style=\"--shiki-light:#0184BC;--shiki-dark:#E1E4E8\">:\u003C/span>\u003Cspan style=\"--shiki-light:#A626A4;--shiki-dark:#F97583\"> async\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\"> (\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#FFAB70\">state\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">, \u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#FFAB70\">args\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">) \u003C/span>\u003Cspan style=\"--shiki-light:#A626A4;--shiki-dark:#F97583\">=>\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\"> {\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#A626A4;--shiki-dark:#F97583\">    const\u003C/span>\u003Cspan style=\"--shiki-light:#986801;--shiki-dark:#79B8FF\"> user\u003C/span>\u003Cspan style=\"--shiki-light:#0184BC;--shiki-dark:#F97583\"> =\u003C/span>\u003Cspan style=\"--shiki-light:#A626A4;--shiki-dark:#F97583\"> await\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\"> state.\u003C/span>\u003Cspan style=\"--shiki-light:#4078F2;--shiki-dark:#B392F0\">user\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">();\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#A626A4;--shiki-dark:#F97583\">    if\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\"> (\u003C/span>\u003Cspan style=\"--shiki-light:#0184BC;--shiki-dark:#F97583\">!\u003C/span>\u003Cspan style=\"--shiki-light:#E45649;--shiki-dark:#E1E4E8\">user\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">) {\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#A626A4;--shiki-dark:#F97583\">      return\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\"> { \u003C/span>\u003Cspan style=\"--shiki-light:#E45649;--shiki-dark:#E1E4E8\">status\u003C/span>\u003Cspan style=\"--shiki-light:#0184BC;--shiki-dark:#E1E4E8\">:\u003C/span>\u003Cspan style=\"--shiki-light:#50A14F;--shiki-dark:#9ECBFF\"> 'error'\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">, \u003C/span>\u003Cspan style=\"--shiki-light:#E45649;--shiki-dark:#E1E4E8\">error\u003C/span>\u003Cspan style=\"--shiki-light:#0184BC;--shiki-dark:#E1E4E8\">:\u003C/span>\u003Cspan style=\"--shiki-light:#50A14F;--shiki-dark:#9ECBFF\"> 'This thread has no user.'\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\"> };\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">    }\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#A626A4;--shiki-dark:#F97583\">    const\u003C/span>\u003Cspan style=\"--shiki-light:#986801;--shiki-dark:#79B8FF\"> response\u003C/span>\u003Cspan style=\"--shiki-light:#0184BC;--shiki-dark:#F97583\"> =\u003C/span>\u003Cspan style=\"--shiki-light:#A626A4;--shiki-dark:#F97583\"> await\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\"> user.\u003C/span>\u003Cspan style=\"--shiki-light:#4078F2;--shiki-dark:#B392F0\">call\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">(\u003C/span>\u003Cspan style=\"--shiki-light:#50A14F;--shiki-dark:#9ECBFF\">'quota/spend'\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">, { \u003C/span>\u003Cspan style=\"--shiki-light:#E45649;--shiki-dark:#E1E4E8\">amount\u003C/span>\u003Cspan style=\"--shiki-light:#0184BC;--shiki-dark:#E1E4E8\">:\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\"> args.\u003C/span>\u003Cspan style=\"--shiki-light:#E45649;--shiki-dark:#E1E4E8\">amount\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\"> });\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#A626A4;--shiki-dark:#F97583\">    if\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\"> (\u003C/span>\u003Cspan style=\"--shiki-light:#0184BC;--shiki-dark:#F97583\">!\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">response.\u003C/span>\u003Cspan style=\"--shiki-light:#E45649;--shiki-dark:#E1E4E8\">ok\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">) {\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#A626A4;--shiki-dark:#F97583\">      return\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\"> { \u003C/span>\u003Cspan style=\"--shiki-light:#E45649;--shiki-dark:#E1E4E8\">status\u003C/span>\u003Cspan style=\"--shiki-light:#0184BC;--shiki-dark:#E1E4E8\">:\u003C/span>\u003Cspan style=\"--shiki-light:#50A14F;--shiki-dark:#9ECBFF\"> 'error'\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">, \u003C/span>\u003Cspan style=\"--shiki-light:#E45649;--shiki-dark:#E1E4E8\">error\u003C/span>\u003Cspan style=\"--shiki-light:#0184BC;--shiki-dark:#E1E4E8\">:\u003C/span>\u003Cspan style=\"--shiki-light:#50A14F;--shiki-dark:#9ECBFF\"> 'The daily quota is used up.'\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\"> };\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">    }\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#A626A4;--shiki-dark:#F97583\">    return\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\"> { \u003C/span>\u003Cspan style=\"--shiki-light:#E45649;--shiki-dark:#E1E4E8\">status\u003C/span>\u003Cspan style=\"--shiki-light:#0184BC;--shiki-dark:#E1E4E8\">:\u003C/span>\u003Cspan style=\"--shiki-light:#50A14F;--shiki-dark:#9ECBFF\"> 'success'\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">, \u003C/span>\u003Cspan style=\"--shiki-light:#E45649;--shiki-dark:#E1E4E8\">result\u003C/span>\u003Cspan style=\"--shiki-light:#0184BC;--shiki-dark:#E1E4E8\">:\u003C/span>\u003Cspan style=\"--shiki-light:#A626A4;--shiki-dark:#F97583\"> await\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\"> response.\u003C/span>\u003Cspan style=\"--shiki-light:#4078F2;--shiki-dark:#B392F0\">text\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">() };\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">  },\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">});\u003C/span>\u003C/span>\u003C/code>\u003C/pre>\u003C/figure>\n\u003Cul>\n\u003Cli>\u003Ccode>call()\u003C/code> sends a \u003Ccode>POST\u003C/code> request with the payload as a JSON body. It reaches endpoints that handle \u003Ccode>POST\u003C/code>.\u003C/li>\n\u003Cli>\u003Ccode>call()\u003C/code> rejects a path that contains a \u003Ccode>.\u003C/code> or \u003Ccode>..\u003C/code> segment, in plain, percent-encoded, or backslash form.\u003C/li>\n\u003Cli>\u003Ccode>state.user()\u003C/code> returns \u003Ccode>null\u003C/code> for a thread without a user.\u003C/li>\n\u003Cli>A \u003Ccode>ThreadState\u003C/code> that came from \u003Ccode>user.thread()\u003C/code> cannot use \u003Ccode>call()\u003C/code>. The call throws, because the user endpoint that raised the thread is still running.\u003C/li>\n\u003C/ul>\n\u003Cp>The value returned by \u003Ccode>state.user()\u003C/code> also has \u003Ccode>getValue()\u003C/code>, \u003Ccode>setValue()\u003C/code>, \u003Ccode>emit()\u003C/code>, and \u003Ccode>fetch()\u003C/code> for the same user.\u003C/p>\n\u003Ch3 id=\"caller-channel\">\u003Ca class=\"docs-anchor\" href=\"#caller-channel\" aria-label=\"Link to this section\">\u003C/a>Caller channel\u003C/h3>\n\u003Cp>Standard Agents Builder sets the \u003Ccode>X-Standard-Agents-Caller\u003C/code> request header on every user endpoint request. The public routes overwrite any value that the client sent.\u003C/p>\n\u003Cdiv class=\"docs-table\">\u003Ctable>\n\u003Cthead>\n\u003Ctr>\n\u003Cth>Value\u003C/th>\n\u003Cth>Caller\u003C/th>\n\u003C/tr>\n\u003C/thead>\n\u003Ctbody>\n\u003Ctr>\n\u003Ctd>\u003Ccode>user\u003C/code>\u003C/td>\n\u003Ctd>The user's own HTTP request to \u003Ccode>/api/users/me/...\u003C/code>\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>admin\u003C/code>\u003C/td>\n\u003Ctd>An administrator's request to \u003Ccode>/api/users/:id/...\u003C/code>\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>thread\u003C/code>\u003C/td>\n\u003Ctd>\u003Ccode>state.user().call()\u003C/code> from thread code\u003C/td>\n\u003C/tr>\n\u003C/tbody>\n\u003C/table>\u003C/div>\n\u003Cp>Check for \u003Ccode>thread\u003C/code> in an endpoint that only your agents may change:\u003C/p>\n\u003Cfigure class=\"docs-code\" data-docs=\"code\" data-lang=\"typescript\">\u003Cpre class=\"shiki shiki-themes one-light github-dark\" style=\"--shiki-light:#383A42;--shiki-dark:#e1e4e8;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#24292e\" tabindex=\"0\">\u003Ccode>\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#6A737D;--shiki-dark-font-style:inherit\">// agents/users/credits/grant.post.ts\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#A626A4;--shiki-dark:#F97583\">import\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\"> { \u003C/span>\u003Cspan style=\"--shiki-light:#E45649;--shiki-dark:#E1E4E8\">defineUserEndpoint\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\"> } \u003C/span>\u003Cspan style=\"--shiki-light:#A626A4;--shiki-dark:#F97583\">from\u003C/span>\u003Cspan style=\"--shiki-light:#50A14F;--shiki-dark:#9ECBFF\"> '@standardagents/spec'\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">;\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#A626A4;--shiki-dark:#F97583\">export\u003C/span>\u003Cspan style=\"--shiki-light:#E45649;--shiki-dark:#F97583\"> default\u003C/span>\u003Cspan style=\"--shiki-light:#4078F2;--shiki-dark:#B392F0\"> defineUserEndpoint\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">(\u003C/span>\u003Cspan style=\"--shiki-light:#A626A4;--shiki-dark:#F97583\">async\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\"> (\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#FFAB70\">req\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">, \u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#FFAB70\">user\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">) \u003C/span>\u003Cspan style=\"--shiki-light:#A626A4;--shiki-dark:#F97583\">=>\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\"> {\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#A626A4;--shiki-dark:#F97583\">  if\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\"> (req.\u003C/span>\u003Cspan style=\"--shiki-light:#E45649;--shiki-dark:#E1E4E8\">headers\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">.\u003C/span>\u003Cspan style=\"--shiki-light:#4078F2;--shiki-dark:#B392F0\">get\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">(\u003C/span>\u003Cspan style=\"--shiki-light:#50A14F;--shiki-dark:#9ECBFF\">'X-Standard-Agents-Caller'\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">) \u003C/span>\u003Cspan style=\"--shiki-light:#0184BC;--shiki-dark:#F97583\">!==\u003C/span>\u003Cspan style=\"--shiki-light:#50A14F;--shiki-dark:#9ECBFF\"> 'thread'\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">) {\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#A626A4;--shiki-dark:#F97583\">    return\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\"> Response.\u003C/span>\u003Cspan style=\"--shiki-light:#4078F2;--shiki-dark:#B392F0\">json\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">({ \u003C/span>\u003Cspan style=\"--shiki-light:#E45649;--shiki-dark:#E1E4E8\">error\u003C/span>\u003Cspan style=\"--shiki-light:#0184BC;--shiki-dark:#E1E4E8\">:\u003C/span>\u003Cspan style=\"--shiki-light:#50A14F;--shiki-dark:#9ECBFF\"> 'Forbidden'\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\"> }, { \u003C/span>\u003Cspan style=\"--shiki-light:#E45649;--shiki-dark:#E1E4E8\">status\u003C/span>\u003Cspan style=\"--shiki-light:#0184BC;--shiki-dark:#E1E4E8\">:\u003C/span>\u003Cspan style=\"--shiki-light:#986801;--shiki-dark:#79B8FF\"> 403\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\"> });\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">  }\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#A626A4;--shiki-dark:#F97583\">  const\u003C/span>\u003Cspan style=\"--shiki-light:#986801;--shiki-dark:#79B8FF\"> credits\u003C/span>\u003Cspan style=\"--shiki-light:#0184BC;--shiki-dark:#F97583\"> =\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\"> (\u003C/span>\u003Cspan style=\"--shiki-light:#A626A4;--shiki-dark:#F97583\">await\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\"> user.\u003C/span>\u003Cspan style=\"--shiki-light:#4078F2;--shiki-dark:#B392F0\">getValue\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">&#x3C;\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#79B8FF\">number\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">>(\u003C/span>\u003Cspan style=\"--shiki-light:#50A14F;--shiki-dark:#9ECBFF\">'credits'\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">)) \u003C/span>\u003Cspan style=\"--shiki-light:#0184BC;--shiki-dark:#F97583\">??\u003C/span>\u003Cspan style=\"--shiki-light:#986801;--shiki-dark:#79B8FF\"> 0\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">;\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#A626A4;--shiki-dark:#F97583\">  await\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\"> user.\u003C/span>\u003Cspan style=\"--shiki-light:#4078F2;--shiki-dark:#B392F0\">setValue\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">(\u003C/span>\u003Cspan style=\"--shiki-light:#50A14F;--shiki-dark:#9ECBFF\">'credits'\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">, \u003C/span>\u003Cspan style=\"--shiki-light:#E45649;--shiki-dark:#E1E4E8\">credits\u003C/span>\u003Cspan style=\"--shiki-light:#0184BC;--shiki-dark:#F97583\"> +\u003C/span>\u003Cspan style=\"--shiki-light:#986801;--shiki-dark:#79B8FF\"> 1\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">);\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#A626A4;--shiki-dark:#F97583\">  return\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\"> Response.\u003C/span>\u003Cspan style=\"--shiki-light:#4078F2;--shiki-dark:#B392F0\">json\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">({ \u003C/span>\u003Cspan style=\"--shiki-light:#E45649;--shiki-dark:#E1E4E8\">credits\u003C/span>\u003Cspan style=\"--shiki-light:#0184BC;--shiki-dark:#E1E4E8\">:\u003C/span>\u003Cspan style=\"--shiki-light:#E45649;--shiki-dark:#E1E4E8\"> credits\u003C/span>\u003Cspan style=\"--shiki-light:#0184BC;--shiki-dark:#F97583\"> +\u003C/span>\u003Cspan style=\"--shiki-light:#986801;--shiki-dark:#79B8FF\"> 1\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\"> });\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">});\u003C/span>\u003C/span>\u003C/code>\u003C/pre>\u003C/figure>\n\u003Ch3 id=\"administrative-access\">\u003Ca class=\"docs-anchor\" href=\"#administrative-access\" aria-label=\"Link to this section\">\u003C/a>Administrative access\u003C/h3>\n\u003Cp>An administrator can call the same endpoints for another user at \u003Ccode>/api/users/:id/...\u003C/code>. The endpoint runs in the addressed user's scope. A caller that is not an administrator receives HTTP 403.\u003C/p>\n\u003Cp>To guard an endpoint with application roles, see \u003Ca href=\"/docs/core-concepts/api#guarding-user-endpoints-with-application-roles\">application role checks\u003C/a>.\u003C/p>\n\u003Ch2 id=\"user-event-stream\">\u003Ca class=\"docs-anchor\" href=\"#user-event-stream\" aria-label=\"Link to this section\">\u003C/a>User event stream\u003C/h2>\n\u003Cp>The user event stream delivers events from \u003Ccode>user.emit()\u003C/code> and from the handle\nreturned by \u003Ccode>await state.user()\u003C/code> to the user's connected clients.\u003C/p>\n\u003Ch3 id=\"connecting\">\u003Ca class=\"docs-anchor\" href=\"#connecting\" aria-label=\"Link to this section\">\u003C/a>Connecting\u003C/h3>\n\u003Cfigure class=\"docs-code\" data-docs=\"code\" data-lang=\"text\">\u003Cpre class=\"shiki shiki-themes one-light github-dark\" style=\"--shiki-light:#383A42;--shiki-dark:#e1e4e8;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#24292e\" tabindex=\"0\">\u003Ccode>\u003Cspan class=\"line\">\u003Cspan>GET /api/users/me/stream     (WebSocket upgrade)\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan>GET /api/users/:id/stream    (WebSocket upgrade, administrators)\u003C/span>\u003C/span>\u003C/code>\u003C/pre>\u003C/figure>\n\u003Cp>A browser cannot set an \u003Ccode>Authorization\u003C/code> header on a WebSocket. Request a single-use ticket for the exact path and pass it as \u003Ccode>?ticket=\u003C/code>, as the \u003Ca href=\"/docs/api-reference/websocket/stream#connecting\">thread stream\u003C/a> does:\u003C/p>\n\u003Cfigure class=\"docs-code\" data-docs=\"code\" data-lang=\"typescript\">\u003Cpre class=\"shiki shiki-themes one-light github-dark\" style=\"--shiki-light:#383A42;--shiki-dark:#e1e4e8;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#24292e\" tabindex=\"0\">\u003Ccode>\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#A626A4;--shiki-dark:#F97583\">async\u003C/span>\u003Cspan style=\"--shiki-light:#A626A4;--shiki-dark:#F97583\"> function\u003C/span>\u003Cspan style=\"--shiki-light:#4078F2;--shiki-dark:#B392F0\"> connectUserStream\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">(\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#FFAB70\">origin\u003C/span>\u003Cspan style=\"--shiki-light:#0184BC;--shiki-dark:#F97583\">:\u003C/span>\u003Cspan style=\"--shiki-light:#0184BC;--shiki-dark:#79B8FF\"> string\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">, \u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#FFAB70\">token\u003C/span>\u003Cspan style=\"--shiki-light:#0184BC;--shiki-dark:#F97583\">:\u003C/span>\u003Cspan style=\"--shiki-light:#0184BC;--shiki-dark:#79B8FF\"> string\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">)\u003C/span>\u003Cspan style=\"--shiki-light:#0184BC;--shiki-dark:#F97583\">:\u003C/span>\u003Cspan style=\"--shiki-light:#C18401;--shiki-dark:#B392F0\"> Promise\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">&#x3C;\u003C/span>\u003Cspan style=\"--shiki-light:#C18401;--shiki-dark:#B392F0\">WebSocket\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">> {\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#A626A4;--shiki-dark:#F97583\">  const\u003C/span>\u003Cspan style=\"--shiki-light:#986801;--shiki-dark:#79B8FF\"> pathname\u003C/span>\u003Cspan style=\"--shiki-light:#0184BC;--shiki-dark:#F97583\"> =\u003C/span>\u003Cspan style=\"--shiki-light:#50A14F;--shiki-dark:#9ECBFF\"> '/api/users/me/stream'\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">;\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#A626A4;--shiki-dark:#F97583\">  const\u003C/span>\u003Cspan style=\"--shiki-light:#986801;--shiki-dark:#79B8FF\"> response\u003C/span>\u003Cspan style=\"--shiki-light:#0184BC;--shiki-dark:#F97583\"> =\u003C/span>\u003Cspan style=\"--shiki-light:#A626A4;--shiki-dark:#F97583\"> await\u003C/span>\u003Cspan style=\"--shiki-light:#4078F2;--shiki-dark:#B392F0\"> fetch\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">(\u003C/span>\u003Cspan style=\"--shiki-light:#50A14F;--shiki-dark:#9ECBFF\">`\u003C/span>\u003Cspan style=\"--shiki-light:#CA1243;--shiki-dark:#9ECBFF\">${\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">origin\u003C/span>\u003Cspan style=\"--shiki-light:#CA1243;--shiki-dark:#9ECBFF\">}\u003C/span>\u003Cspan style=\"--shiki-light:#50A14F;--shiki-dark:#9ECBFF\">/api/auth/websocket-ticket`\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">, {\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#E45649;--shiki-dark:#E1E4E8\">    method\u003C/span>\u003Cspan style=\"--shiki-light:#0184BC;--shiki-dark:#E1E4E8\">:\u003C/span>\u003Cspan style=\"--shiki-light:#50A14F;--shiki-dark:#9ECBFF\"> 'POST'\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">,\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#E45649;--shiki-dark:#E1E4E8\">    headers\u003C/span>\u003Cspan style=\"--shiki-light:#0184BC;--shiki-dark:#E1E4E8\">:\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\"> { \u003C/span>\u003Cspan style=\"--shiki-light:#E45649;--shiki-dark:#E1E4E8\">Authorization\u003C/span>\u003Cspan style=\"--shiki-light:#0184BC;--shiki-dark:#E1E4E8\">:\u003C/span>\u003Cspan style=\"--shiki-light:#50A14F;--shiki-dark:#9ECBFF\"> `Bearer \u003C/span>\u003Cspan style=\"--shiki-light:#CA1243;--shiki-dark:#9ECBFF\">${\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">token\u003C/span>\u003Cspan style=\"--shiki-light:#CA1243;--shiki-dark:#9ECBFF\">}\u003C/span>\u003Cspan style=\"--shiki-light:#50A14F;--shiki-dark:#9ECBFF\">`\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">, \u003C/span>\u003Cspan style=\"--shiki-light:#50A14F;--shiki-dark:#9ECBFF\">'Content-Type'\u003C/span>\u003Cspan style=\"--shiki-light:#0184BC;--shiki-dark:#E1E4E8\">:\u003C/span>\u003Cspan style=\"--shiki-light:#50A14F;--shiki-dark:#9ECBFF\"> 'application/json'\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\"> },\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#E45649;--shiki-dark:#E1E4E8\">    body\u003C/span>\u003Cspan style=\"--shiki-light:#0184BC;--shiki-dark:#E1E4E8\">:\u003C/span>\u003Cspan style=\"--shiki-light:#986801;--shiki-dark:#79B8FF\"> JSON\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">.\u003C/span>\u003Cspan style=\"--shiki-light:#4078F2;--shiki-dark:#B392F0\">stringify\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">({ pathname }),\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">  });\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#A626A4;--shiki-dark:#F97583\">  const\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\"> { \u003C/span>\u003Cspan style=\"--shiki-light:#986801;--shiki-dark:#79B8FF\">ticket\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\"> } \u003C/span>\u003Cspan style=\"--shiki-light:#0184BC;--shiki-dark:#F97583\">=\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\"> (\u003C/span>\u003Cspan style=\"--shiki-light:#A626A4;--shiki-dark:#F97583\">await\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\"> response.\u003C/span>\u003Cspan style=\"--shiki-light:#4078F2;--shiki-dark:#B392F0\">json\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">()) \u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#F97583\">as\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\"> { \u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#FFAB70\">ticket\u003C/span>\u003Cspan style=\"--shiki-light:#0184BC;--shiki-dark:#F97583\">:\u003C/span>\u003Cspan style=\"--shiki-light:#0184BC;--shiki-dark:#79B8FF\"> string\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\"> };\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#A626A4;--shiki-dark:#F97583\">  const\u003C/span>\u003Cspan style=\"--shiki-light:#986801;--shiki-dark:#79B8FF\"> ws\u003C/span>\u003Cspan style=\"--shiki-light:#0184BC;--shiki-dark:#F97583\"> =\u003C/span>\u003Cspan style=\"--shiki-light:#A626A4;--shiki-dark:#F97583\"> new\u003C/span>\u003Cspan style=\"--shiki-light:#4078F2;--shiki-dark:#B392F0\"> WebSocket\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">(\u003C/span>\u003Cspan style=\"--shiki-light:#50A14F;--shiki-dark:#9ECBFF\">`\u003C/span>\u003Cspan style=\"--shiki-light:#CA1243;--shiki-dark:#9ECBFF\">${\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">origin\u003C/span>\u003Cspan style=\"--shiki-light:#50A14F;--shiki-dark:#9ECBFF\">.\u003C/span>\u003Cspan style=\"--shiki-light:#4078F2;--shiki-dark:#B392F0\">replace\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#9ECBFF\">(\u003C/span>\u003Cspan style=\"--shiki-light:#CA1243;--shiki-dark:#9ECBFF\">/\u003C/span>\u003Cspan style=\"--shiki-light:#A626A4;--shiki-dark:#F97583\">^\u003C/span>\u003Cspan style=\"--shiki-light:#0184BC;--shiki-dark:#DBEDFF\">http\u003C/span>\u003Cspan style=\"--shiki-light:#CA1243;--shiki-dark:#9ECBFF\">/\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#9ECBFF\">,\u003C/span>\u003Cspan style=\"--shiki-light:#CA1243;--shiki-dark:#9ECBFF\"> '\u003C/span>\u003Cspan style=\"--shiki-light:#50A14F;--shiki-dark:#9ECBFF\">ws\u003C/span>\u003Cspan style=\"--shiki-light:#CA1243;--shiki-dark:#9ECBFF\">'\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#9ECBFF\">)\u003C/span>\u003Cspan style=\"--shiki-light:#CA1243;--shiki-dark:#9ECBFF\">}${\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">pathname\u003C/span>\u003Cspan style=\"--shiki-light:#CA1243;--shiki-dark:#9ECBFF\">}\u003C/span>\u003Cspan style=\"--shiki-light:#50A14F;--shiki-dark:#9ECBFF\">?ticket=\u003C/span>\u003Cspan style=\"--shiki-light:#CA1243;--shiki-dark:#9ECBFF\">${\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">ticket\u003C/span>\u003Cspan style=\"--shiki-light:#CA1243;--shiki-dark:#9ECBFF\">}\u003C/span>\u003Cspan style=\"--shiki-light:#50A14F;--shiki-dark:#9ECBFF\">`\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">);\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">  ws.\u003C/span>\u003Cspan style=\"--shiki-light:#4078F2;--shiki-dark:#B392F0\">onmessage\u003C/span>\u003Cspan style=\"--shiki-light:#0184BC;--shiki-dark:#F97583\"> =\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\"> (\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#FFAB70\">message\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">) \u003C/span>\u003Cspan style=\"--shiki-light:#A626A4;--shiki-dark:#F97583\">=>\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\"> {\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#A626A4;--shiki-dark:#F97583\">    if\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\"> (message.\u003C/span>\u003Cspan style=\"--shiki-light:#E45649;--shiki-dark:#E1E4E8\">data\u003C/span>\u003Cspan style=\"--shiki-light:#0184BC;--shiki-dark:#F97583\"> ===\u003C/span>\u003Cspan style=\"--shiki-light:#50A14F;--shiki-dark:#9ECBFF\"> 'stream_pong'\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">) \u003C/span>\u003Cspan style=\"--shiki-light:#A626A4;--shiki-dark:#F97583\">return\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">;\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#A626A4;--shiki-dark:#F97583\">    const\u003C/span>\u003Cspan style=\"--shiki-light:#986801;--shiki-dark:#79B8FF\"> frame\u003C/span>\u003Cspan style=\"--shiki-light:#0184BC;--shiki-dark:#F97583\"> =\u003C/span>\u003Cspan style=\"--shiki-light:#986801;--shiki-dark:#79B8FF\"> JSON\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">.\u003C/span>\u003Cspan style=\"--shiki-light:#4078F2;--shiki-dark:#B392F0\">parse\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">(\u003C/span>\u003Cspan style=\"--shiki-light:#4078F2;--shiki-dark:#B392F0\">String\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">(message.\u003C/span>\u003Cspan style=\"--shiki-light:#E45649;--shiki-dark:#E1E4E8\">data\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">)) \u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#F97583\">as\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\"> { \u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#FFAB70\">event\u003C/span>\u003Cspan style=\"--shiki-light:#0184BC;--shiki-dark:#F97583\">:\u003C/span>\u003Cspan style=\"--shiki-light:#0184BC;--shiki-dark:#79B8FF\"> string\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">; \u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#FFAB70\">data\u003C/span>\u003Cspan style=\"--shiki-light:#0184BC;--shiki-dark:#F97583\">?:\u003C/span>\u003Cspan style=\"--shiki-light:#0184BC;--shiki-dark:#79B8FF\"> unknown\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\"> };\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">    console.\u003C/span>\u003Cspan style=\"--shiki-light:#4078F2;--shiki-dark:#B392F0\">log\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">(frame.\u003C/span>\u003Cspan style=\"--shiki-light:#E45649;--shiki-dark:#E1E4E8\">event\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">, frame.\u003C/span>\u003Cspan style=\"--shiki-light:#E45649;--shiki-dark:#E1E4E8\">data\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">);\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">  };\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#A626A4;--shiki-dark:#F97583\">  return\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\"> ws;\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">}\u003C/span>\u003C/span>\u003C/code>\u003C/pre>\u003C/figure>\n\u003Cp>A request without a WebSocket upgrade returns HTTP 426. A user can hold several connections at the same time, one for each device or tab.\u003C/p>\n\u003Ch3 id=\"frames\">\u003Ca class=\"docs-anchor\" href=\"#frames\" aria-label=\"Link to this section\">\u003C/a>Frames\u003C/h3>\n\u003Cp>Each frame is a JSON object:\u003C/p>\n\u003Cfigure class=\"docs-code\" data-docs=\"code\" data-lang=\"typescript\">\u003Cpre class=\"shiki shiki-themes one-light github-dark\" style=\"--shiki-light:#383A42;--shiki-dark:#e1e4e8;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#24292e\" tabindex=\"0\">\u003Ccode>\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#A626A4;--shiki-dark:#F97583\">interface\u003C/span>\u003Cspan style=\"--shiki-light:#C18401;--shiki-dark:#B392F0\"> UserStreamFrame\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\"> {\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#FFAB70\">  event\u003C/span>\u003Cspan style=\"--shiki-light:#0184BC;--shiki-dark:#F97583\">:\u003C/span>\u003Cspan style=\"--shiki-light:#0184BC;--shiki-dark:#79B8FF\"> string\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">;\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#FFAB70\">  data\u003C/span>\u003Cspan style=\"--shiki-light:#0184BC;--shiki-dark:#F97583\">?:\u003C/span>\u003Cspan style=\"--shiki-light:#0184BC;--shiki-dark:#79B8FF\"> unknown\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">;\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">}\u003C/span>\u003C/span>\u003C/code>\u003C/pre>\u003C/figure>\n\u003Cfigure class=\"docs-code\" data-docs=\"code\" data-lang=\"json\">\u003Cpre class=\"shiki shiki-themes one-light github-dark\" style=\"--shiki-light:#383A42;--shiki-dark:#e1e4e8;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#24292e\" tabindex=\"0\">\u003Ccode>\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">{ \u003C/span>\u003Cspan style=\"--shiki-light:#E45649;--shiki-dark:#79B8FF\">\"event\"\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">: \u003C/span>\u003Cspan style=\"--shiki-light:#50A14F;--shiki-dark:#9ECBFF\">\"quota_changed\"\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">, \u003C/span>\u003Cspan style=\"--shiki-light:#E45649;--shiki-dark:#79B8FF\">\"data\"\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">: { \u003C/span>\u003Cspan style=\"--shiki-light:#E45649;--shiki-dark:#79B8FF\">\"used\"\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">: \u003C/span>\u003Cspan style=\"--shiki-light:#986801;--shiki-dark:#79B8FF\">12\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">, \u003C/span>\u003Cspan style=\"--shiki-light:#E45649;--shiki-dark:#79B8FF\">\"limit\"\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">: \u003C/span>\u003Cspan style=\"--shiki-light:#986801;--shiki-dark:#79B8FF\">100\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\"> } }\u003C/span>\u003C/span>\u003C/code>\u003C/pre>\u003C/figure>\n\u003Cul>\n\u003Cli>Standard Agents Builder sends \u003Ccode>{ \"event\": \"stream_ready\", \"data\": { \"client_id\": \"...\" } }\u003C/code> as the first frame of each connection. Pass \u003Ccode>?client_id=\u003C/code> to set that ID, and \u003Ccode>?client_kind=\u003C/code> to label the client.\u003C/li>\n\u003Cli>\u003Ccode>data\u003C/code> is absent when the event was emitted without data.\u003C/li>\n\u003Cli>Frames arrive in the order the user's code emitted them.\u003C/li>\n\u003Cli>Choose event names that include your package or feature name, such as \u003Ccode>quota_changed\u003C/code>.\u003C/li>\n\u003C/ul>\n\u003Ch3 id=\"delivery\">\u003Ca class=\"docs-anchor\" href=\"#delivery\" aria-label=\"Link to this section\">\u003C/a>Delivery\u003C/h3>\n\u003Cp>The stream is live only. It stores no history and has no acknowledgements. A client that was not connected when an event was emitted does not receive that event.\u003C/p>\n\u003Cp>After each connect and reconnect, buffer incoming events while you read the\ncurrent state from your user endpoints. Reconcile buffered events with the\nsnapshot before you apply later events.\u003C/p>\n\u003Ch3 id=\"keepalive\">\u003Ca class=\"docs-anchor\" href=\"#keepalive\" aria-label=\"Link to this section\">\u003C/a>Keepalive\u003C/h3>\n\u003Cp>Send the string \u003Ccode>stream_ping\u003C/code> at intervals. The runtime answers \u003Ccode>stream_pong\u003C/code>.\u003C/p>\n\u003Ch2 id=\"user-rooms\">\u003Ca class=\"docs-anchor\" href=\"#user-rooms\" aria-label=\"Link to this section\">\u003C/a>User rooms\u003C/h2>\n\u003Ch3 id=\"what-is-a-room\">\u003Ca class=\"docs-anchor\" href=\"#what-is-a-room\" aria-label=\"Link to this section\">\u003C/a>What is a room?\u003C/h3>\n\u003Cp>A user room is a WebSocket hub for one user's connected clients. A project has at most one room. The room adds three things to the user event stream:\u003C/p>\n\u003Cul>\n\u003Cli>\u003Cstrong>Client frames\u003C/strong>: a client sends a frame on the socket, and your code handles it in the user scope\u003C/li>\n\u003Cli>\u003Cstrong>Targeted delivery\u003C/strong>: your code sends a frame to one client by its ID\u003C/li>\n\u003Cli>\u003Cstrong>Presence callbacks\u003C/strong>: your code runs when a client attaches or detaches\u003C/li>\n\u003C/ul>\n\u003Cp>Room frames and presence callbacks run one at a time for each user, in the same queue as \u003Ca href=\"#one-run-at-a-time-for-each-user\">user endpoints\u003C/a>.\u003C/p>\n\u003Ch3 id=\"when-a-project-needs-a-room\">\u003Ca class=\"docs-anchor\" href=\"#when-a-project-needs-a-room\" aria-label=\"Link to this section\">\u003C/a>When a project needs a room\u003C/h3>\n\u003Cp>Define a room when one of these is true:\u003C/p>\n\u003Cul>\n\u003Cli>Clients must send messages to your user-scope code over an open socket.\u003C/li>\n\u003Cli>Your code must address one device or tab of the user.\u003C/li>\n\u003Cli>Your code must know which clients are connected.\u003C/li>\n\u003Cli>Your code must verify a client before that client receives frames.\u003C/li>\n\u003C/ul>\n\u003Cp>The user event stream is enough when your code only pushes events to all of the user's clients and the clients send their changes through user endpoints.\u003C/p>\n\u003Ch3 id=\"defining-a-room\">\u003Ca class=\"docs-anchor\" href=\"#defining-a-room\" aria-label=\"Link to this section\">\u003C/a>Defining a room\u003C/h3>\n\u003Cp>Create \u003Ccode>agents/users/&#x3C;package>/room.ts\u003C/code> and export a \u003Ccode>defineUserRoom\u003C/code> call as the default export:\u003C/p>\n\u003Cfigure class=\"docs-code\" data-docs=\"code\" data-lang=\"typescript\">\u003Cpre class=\"shiki shiki-themes one-light github-dark\" style=\"--shiki-light:#383A42;--shiki-dark:#e1e4e8;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#24292e\" tabindex=\"0\">\u003Ccode>\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#6A737D;--shiki-dark-font-style:inherit\">// agents/users/my_package/room.ts\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#A626A4;--shiki-dark:#F97583\">import\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\"> { \u003C/span>\u003Cspan style=\"--shiki-light:#E45649;--shiki-dark:#E1E4E8\">defineUserRoom\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\"> } \u003C/span>\u003Cspan style=\"--shiki-light:#A626A4;--shiki-dark:#F97583\">from\u003C/span>\u003Cspan style=\"--shiki-light:#50A14F;--shiki-dark:#9ECBFF\"> '@standardagents/spec'\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">;\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#A626A4;--shiki-dark:#F97583\">export\u003C/span>\u003Cspan style=\"--shiki-light:#E45649;--shiki-dark:#F97583\"> default\u003C/span>\u003Cspan style=\"--shiki-light:#4078F2;--shiki-dark:#B392F0\"> defineUserRoom\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">({\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#A626A4;--shiki-dark:#F97583\">  async\u003C/span>\u003Cspan style=\"--shiki-light:#4078F2;--shiki-dark:#B392F0\"> onFrame\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">(\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#FFAB70\">user\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">, \u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#FFAB70\">connection\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">, \u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#FFAB70\">frame\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">) {\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#A626A4;--shiki-dark:#F97583\">    if\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\"> (frame.\u003C/span>\u003Cspan style=\"--shiki-light:#E45649;--shiki-dark:#E1E4E8\">op\u003C/span>\u003Cspan style=\"--shiki-light:#0184BC;--shiki-dark:#F97583\"> ===\u003C/span>\u003Cspan style=\"--shiki-light:#50A14F;--shiki-dark:#9ECBFF\"> 'ping'\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">) {\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#A626A4;--shiki-dark:#F97583\">      await\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\"> user.\u003C/span>\u003Cspan style=\"--shiki-light:#4078F2;--shiki-dark:#B392F0\">send\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">(connection.\u003C/span>\u003Cspan style=\"--shiki-light:#E45649;--shiki-dark:#E1E4E8\">clientId\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">, { \u003C/span>\u003Cspan style=\"--shiki-light:#E45649;--shiki-dark:#E1E4E8\">type\u003C/span>\u003Cspan style=\"--shiki-light:#0184BC;--shiki-dark:#E1E4E8\">:\u003C/span>\u003Cspan style=\"--shiki-light:#50A14F;--shiki-dark:#9ECBFF\"> 'room'\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">, \u003C/span>\u003Cspan style=\"--shiki-light:#E45649;--shiki-dark:#E1E4E8\">id\u003C/span>\u003Cspan style=\"--shiki-light:#0184BC;--shiki-dark:#E1E4E8\">:\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\"> frame.\u003C/span>\u003Cspan style=\"--shiki-light:#E45649;--shiki-dark:#E1E4E8\">id\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">, \u003C/span>\u003Cspan style=\"--shiki-light:#E45649;--shiki-dark:#E1E4E8\">ok\u003C/span>\u003Cspan style=\"--shiki-light:#0184BC;--shiki-dark:#E1E4E8\">:\u003C/span>\u003Cspan style=\"--shiki-light:#986801;--shiki-dark:#79B8FF\"> true\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\"> });\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">    }\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">  },\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#A626A4;--shiki-dark:#F97583\">  async\u003C/span>\u003Cspan style=\"--shiki-light:#4078F2;--shiki-dark:#B392F0\"> onPresence\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">(\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#FFAB70\">user\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">, \u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#FFAB70\">event\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">) {\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">    user.\u003C/span>\u003Cspan style=\"--shiki-light:#4078F2;--shiki-dark:#B392F0\">emit\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">(\u003C/span>\u003Cspan style=\"--shiki-light:#50A14F;--shiki-dark:#9ECBFF\">'my_package.presence'\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">, {\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#E45649;--shiki-dark:#E1E4E8\">      client\u003C/span>\u003Cspan style=\"--shiki-light:#0184BC;--shiki-dark:#E1E4E8\">:\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\"> event.\u003C/span>\u003Cspan style=\"--shiki-light:#E45649;--shiki-dark:#E1E4E8\">connection\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">.\u003C/span>\u003Cspan style=\"--shiki-light:#E45649;--shiki-dark:#E1E4E8\">clientId\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">,\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#E45649;--shiki-dark:#E1E4E8\">      online\u003C/span>\u003Cspan style=\"--shiki-light:#0184BC;--shiki-dark:#E1E4E8\">:\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\"> event.\u003C/span>\u003Cspan style=\"--shiki-light:#E45649;--shiki-dark:#E1E4E8\">kind\u003C/span>\u003Cspan style=\"--shiki-light:#0184BC;--shiki-dark:#F97583\"> ===\u003C/span>\u003Cspan style=\"--shiki-light:#50A14F;--shiki-dark:#9ECBFF\"> 'attached'\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">,\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">    });\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">  },\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">});\u003C/span>\u003C/span>\u003C/code>\u003C/pre>\u003C/figure>\n\u003Cul>\n\u003Cli>\u003Ccode>onFrame\u003C/code> runs for each room frame that a client sends.\u003C/li>\n\u003Cli>\u003Ccode>onPresence\u003C/code> is optional. It runs when a client attaches and when a client detaches. In the default admission mode, a client that reconnects within 30 seconds causes no presence change.\u003C/li>\n\u003Cli>A room file is not an HTTP route. A request to its path returns HTTP 404.\u003C/li>\n\u003Cli>When two files define a room, Standard Agents Builder refuses room connections with HTTP 503. User endpoints and the user event stream continue to work.\u003C/li>\n\u003C/ul>\n\u003Ch3 id=\"hub-route\">\u003Ca class=\"docs-anchor\" href=\"#hub-route\" aria-label=\"Link to this section\">\u003C/a>Hub route\u003C/h3>\n\u003Cfigure class=\"docs-code\" data-docs=\"code\" data-lang=\"text\">\u003Cpre class=\"shiki shiki-themes one-light github-dark\" style=\"--shiki-light:#383A42;--shiki-dark:#e1e4e8;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#24292e\" tabindex=\"0\">\u003Ccode>\u003Cspan class=\"line\">\u003Cspan>GET /api/users/me/hub?client_id=...     (WebSocket upgrade)\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan>GET /api/users/:id/hub?client_id=...    (WebSocket upgrade, administrators)\u003C/span>\u003C/span>\u003C/code>\u003C/pre>\u003C/figure>\n\u003Cp>Authenticate with a single-use ticket for the exact path, as the \u003Ca href=\"#connecting\">user event stream\u003C/a> does. A request without a WebSocket upgrade returns HTTP 426.\u003C/p>\n\u003Cul>\n\u003Cli>Standard Agents Builder sends \u003Ccode>{ \"type\": \"hub_ready\" }\u003C/code> as the first frame.\u003C/li>\n\u003Cli>\u003Ccode>client_id\u003C/code> identifies the client. A connection without \u003Ccode>client_id\u003C/code> is not a room connection. The optional \u003Ccode>client_kind\u003C/code> labels the client. Each value is trimmed and cut to 256 characters.\u003C/li>\n\u003Cli>A room frame is a JSON object with \u003Ccode>\"type\": \"room\"\u003C/code> and a string \u003Ccode>id\u003C/code> of 1 to 256 characters, such as \u003Ccode>{ \"type\": \"room\", \"id\": \"1\", \"op\": \"ping\" }\u003C/code>. The other fields belong to your protocol. The runtime sends no acknowledgement, so reply with \u003Ccode>user.send()\u003C/code> and repeat the \u003Ccode>id\u003C/code>.\u003C/li>\n\u003Cli>A room frame in a project without a room receives \u003Ccode>{ \"type\": \"room\", \"id\": \"...\", \"error\": \"no_room\" }\u003C/code>.\u003C/li>\n\u003Cli>\u003Ccode>user.emit()\u003C/code> events also reach room connections, as \u003Ccode>{ \"event\": \"...\", \"data\": ... }\u003C/code> frames.\u003C/li>\n\u003Cli>In the default admission mode, a new connection with the same \u003Ccode>client_id\u003C/code> replaces the earlier one. The earlier socket receives \u003Ccode>{ \"type\": \"superseded\" }\u003C/code> and closes with code 4001. A client that receives this frame stops its reconnect attempts.\u003C/li>\n\u003C/ul>\n\u003Ch3 id=\"admission-rules\">\u003Ca class=\"docs-anchor\" href=\"#admission-rules\" aria-label=\"Link to this section\">\u003C/a>Admission rules\u003C/h3>\n\u003Cp>A room has its own admission mode. The default is \u003Ccode>automatic\u003C/code>: each connection with a \u003Ccode>client_id\u003C/code> joins the room at once.\u003C/p>\n\u003Cp>With \u003Ccode>admission: 'explicit'\u003C/code>, a new connection starts as pending. A pending connection receives no \u003Ccode>user.send()\u003C/code> frames and no \u003Ccode>user.emit()\u003C/code> events, and \u003Ccode>user.clients()\u003C/code> does not list it. \u003Ccode>onFrame\u003C/code> still receives its frames, so your code can verify a proof from the client. Reply to a pending connection with \u003Ccode>user.roomAttachments.send()\u003C/code>, admit it with \u003Ccode>user.roomAttachments.admit()\u003C/code>, or reject it with \u003Ccode>user.roomAttachments.close()\u003C/code>. Each call takes \u003Ccode>connection.attachmentId\u003C/code>. An admitted connection replaces the earlier admitted connection with the same \u003Ccode>client_id\u003C/code>. In this mode a connection without \u003Ccode>client_id\u003C/code> returns HTTP 400.\u003C/p>\n\u003Cp>A room can reserve user-value keys with \u003Ccode>valuePrefix\u003C/code>. Client-facing KV routes\nreject writes under that prefix. Changes to those keys do not run\n\u003Ccode>user_value_changed\u003C/code>.\u003C/p>\n\u003Cp>Room connections do not run the \u003Ccode>user_client_attached\u003C/code> and \u003Ccode>user_client_detached\u003C/code> hooks. Those hooks belong to the user event stream.\u003C/p>\n\u003Ch2 id=\"user-lifecycle-hooks\">\u003Ca class=\"docs-anchor\" href=\"#user-lifecycle-hooks\" aria-label=\"Link to this section\">\u003C/a>User lifecycle hooks\u003C/h2>\n\u003Cp>Three hook types run in the user scope. Each receives a \u003Ccode>UserState\u003C/code> in place of a \u003Ccode>ThreadState\u003C/code>.\u003C/p>\n\u003Cdiv class=\"docs-table\">\u003Ctable>\n\u003Cthead>\n\u003Ctr>\n\u003Cth>Hook\u003C/th>\n\u003Cth>Arguments\u003C/th>\n\u003Cth>Runs when\u003C/th>\n\u003C/tr>\n\u003C/thead>\n\u003Ctbody>\n\u003Ctr>\n\u003Ctd>\u003Ccode>user_client_attached\u003C/code>\u003C/td>\n\u003Ctd>\u003Ccode>(user, client)\u003C/code>\u003C/td>\n\u003Ctd>A client connects to the user event stream\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>user_client_detached\u003C/code>\u003C/td>\n\u003Ctd>\u003Ccode>(user, client)\u003C/code>\u003C/td>\n\u003Ctd>A client disconnects from the user event stream\u003C/td>\n\u003C/tr>\n\u003Ctr>\n\u003Ctd>\u003Ccode>user_value_changed\u003C/code>\u003C/td>\n\u003Ctd>\u003Ccode>(user, key)\u003C/code>\u003C/td>\n\u003Ctd>A user value changes\u003C/td>\n\u003C/tr>\n\u003C/tbody>\n\u003C/table>\u003C/div>\n\u003Cp>\u003Ccode>client\u003C/code> is \u003Ccode>{ id: string; kind?: string }\u003C/code>.\u003C/p>\n\u003Cfigure class=\"docs-code\" data-docs=\"code\" data-lang=\"typescript\">\u003Cpre class=\"shiki shiki-themes one-light github-dark\" style=\"--shiki-light:#383A42;--shiki-dark:#e1e4e8;--shiki-light-bg:#FAFAFA;--shiki-dark-bg:#24292e\" tabindex=\"0\">\u003Ccode>\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#A0A1A7;--shiki-light-font-style:italic;--shiki-dark:#6A737D;--shiki-dark-font-style:inherit\">// agents/hooks/announce_client.ts\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#A626A4;--shiki-dark:#F97583\">import\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\"> { \u003C/span>\u003Cspan style=\"--shiki-light:#E45649;--shiki-dark:#E1E4E8\">defineHook\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\"> } \u003C/span>\u003Cspan style=\"--shiki-light:#A626A4;--shiki-dark:#F97583\">from\u003C/span>\u003Cspan style=\"--shiki-light:#50A14F;--shiki-dark:#9ECBFF\"> '@standardagents/spec'\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">;\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#A626A4;--shiki-dark:#F97583\">export\u003C/span>\u003Cspan style=\"--shiki-light:#E45649;--shiki-dark:#F97583\"> default\u003C/span>\u003Cspan style=\"--shiki-light:#4078F2;--shiki-dark:#B392F0\"> defineHook\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">({\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#E45649;--shiki-dark:#E1E4E8\">  hook\u003C/span>\u003Cspan style=\"--shiki-light:#0184BC;--shiki-dark:#E1E4E8\">:\u003C/span>\u003Cspan style=\"--shiki-light:#50A14F;--shiki-dark:#9ECBFF\"> 'user_client_attached'\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">,\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#E45649;--shiki-dark:#E1E4E8\">  id\u003C/span>\u003Cspan style=\"--shiki-light:#0184BC;--shiki-dark:#E1E4E8\">:\u003C/span>\u003Cspan style=\"--shiki-light:#50A14F;--shiki-dark:#9ECBFF\"> 'announce_client'\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">,\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#4078F2;--shiki-dark:#B392F0\">  execute\u003C/span>\u003Cspan style=\"--shiki-light:#0184BC;--shiki-dark:#E1E4E8\">:\u003C/span>\u003Cspan style=\"--shiki-light:#A626A4;--shiki-dark:#F97583\"> async\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\"> (\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#FFAB70\">user\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">, \u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#FFAB70\">client\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">) \u003C/span>\u003Cspan style=\"--shiki-light:#A626A4;--shiki-dark:#F97583\">=>\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\"> {\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">    user.\u003C/span>\u003Cspan style=\"--shiki-light:#4078F2;--shiki-dark:#B392F0\">emit\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">(\u003C/span>\u003Cspan style=\"--shiki-light:#50A14F;--shiki-dark:#9ECBFF\">'client_attached'\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">, { \u003C/span>\u003Cspan style=\"--shiki-light:#E45649;--shiki-dark:#E1E4E8\">id\u003C/span>\u003Cspan style=\"--shiki-light:#0184BC;--shiki-dark:#E1E4E8\">:\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\"> client.\u003C/span>\u003Cspan style=\"--shiki-light:#E45649;--shiki-dark:#E1E4E8\">id\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">, \u003C/span>\u003Cspan style=\"--shiki-light:#E45649;--shiki-dark:#E1E4E8\">kind\u003C/span>\u003Cspan style=\"--shiki-light:#0184BC;--shiki-dark:#E1E4E8\">:\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\"> client.\u003C/span>\u003Cspan style=\"--shiki-light:#E45649;--shiki-dark:#E1E4E8\">kind\u003C/span>\u003Cspan style=\"--shiki-light:#0184BC;--shiki-dark:#F97583\"> ??\u003C/span>\u003Cspan style=\"--shiki-light:#986801;--shiki-dark:#79B8FF\"> null\u003C/span>\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\"> });\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">  },\u003C/span>\u003C/span>\n\u003Cspan class=\"line\">\u003Cspan style=\"--shiki-light:#383A42;--shiki-dark:#E1E4E8\">});\u003C/span>\u003C/span>\u003C/code>\u003C/pre>\u003C/figure>\n\u003Cp>A user hook needs no reference from a prompt or an agent. It runs for every user of the project while its file exists. A user hook observes the operation and cannot block it. Standard Agents Builder discards an error that a user hook throws.\u003C/p>\n\u003Ch2 id=\"next-steps\">\u003Ca class=\"docs-anchor\" href=\"#next-steps\" aria-label=\"Link to this section\">\u003C/a>Next steps\u003C/h2>\n\u003Cdiv class=\"docs-card-group\" data-cols=\"2\">\u003Ca class=\"docs-card\" href=\"/docs/core-concepts/api\">\u003Cspan class=\"docs-icon\" data-icon=\"code\">\u003C/span>\u003Cspan class=\"docs-card-title\">API\u003C/span>\u003Cdiv class=\"docs-card-body\">\u003Cp>Create endpoints that operate on one thread\u003C/p>\u003C/div>\u003C/a>\u003Ca class=\"docs-card\" href=\"/docs/core-concepts/credential-grants\">\u003Cspan class=\"docs-icon\" data-icon=\"key\">\u003C/span>\u003Cspan class=\"docs-card-title\">Credential grants\u003C/span>\u003Cdiv class=\"docs-card-body\">\u003Cp>Call an external API with a user credential that your code cannot read\u003C/p>\u003C/div>\u003C/a>\u003Ca class=\"docs-card\" href=\"/docs/core-concepts/thread\">\u003Cspan class=\"docs-icon\" data-icon=\"database\">\u003C/span>\u003Cspan class=\"docs-card-title\">Thread\u003C/span>\u003Cdiv class=\"docs-card-body\">\u003Cp>Read the thread owner's identity with \u003Ccode>state.user()\u003C/code>\u003C/p>\u003C/div>\u003C/a>\u003Ca class=\"docs-card\" href=\"/docs/api-reference/websocket/stream\">\u003Cspan class=\"docs-icon\" data-icon=\"tower-broadcast\">\u003C/span>\u003Cspan class=\"docs-card-title\">WebSocket stream\u003C/span>\u003Cdiv class=\"docs-card-body\">\u003Cp>Receive live frames for one thread\u003C/p>\u003C/div>\u003C/a>\u003Ca class=\"docs-card\" href=\"/docs/core-concepts/hooks\">\u003Cspan class=\"docs-icon\" data-icon=\"webhook\">\u003C/span>\u003Cspan class=\"docs-card-title\">Hooks\u003C/span>\u003Cdiv class=\"docs-card-body\">\u003Cp>Run code at thread lifecycle points\u003C/p>\u003C/div>\u003C/a>\u003C/div>",[9,13,16,20,23,26,29,32,35,38,41,44,47,50,53,56,59,62,65,68,71,74,77],{"id":10,"text":11,"depth":12},"what-is-the-user-scope","What is the user scope?",2,{"id":14,"text":15,"depth":12},"user-endpoints","User endpoints",{"id":17,"text":18,"depth":19},"quick-start","Quick start",3,{"id":21,"text":22,"depth":19},"file-based-routing","File-based routing",{"id":24,"text":25,"depth":19},"handler-function","Handler function",{"id":27,"text":28,"depth":19},"one-run-at-a-time-for-each-user","One run at a time for each user",{"id":30,"text":31,"depth":19},"userstate","UserState",{"id":33,"text":34,"depth":19},"calling-a-user-endpoint-from-a-thread","Calling a user endpoint from a thread",{"id":36,"text":37,"depth":19},"caller-channel","Caller channel",{"id":39,"text":40,"depth":19},"administrative-access","Administrative access",{"id":42,"text":43,"depth":12},"user-event-stream","User event stream",{"id":45,"text":46,"depth":19},"connecting","Connecting",{"id":48,"text":49,"depth":19},"frames","Frames",{"id":51,"text":52,"depth":19},"delivery","Delivery",{"id":54,"text":55,"depth":19},"keepalive","Keepalive",{"id":57,"text":58,"depth":12},"user-rooms","User rooms",{"id":60,"text":61,"depth":19},"what-is-a-room","What is a room?",{"id":63,"text":64,"depth":19},"when-a-project-needs-a-room","When a project needs a room",{"id":66,"text":67,"depth":19},"defining-a-room","Defining a room",{"id":69,"text":70,"depth":19},"hub-route","Hub route",{"id":72,"text":73,"depth":19},"admission-rules","Admission rules",{"id":75,"text":76,"depth":12},"user-lifecycle-hooks","User lifecycle hooks",{"id":78,"text":79,"depth":12},"next-steps","Next steps","core-concepts/users","docs","Core Concepts",{"path":84,"title":85,"navTitle":85},"core-concepts/api","API",{"path":87,"title":88,"navTitle":88},"core-concepts/credential-grants","Credential grants","/docs/core-concepts/users.md","https://github.com/standardagents/agentbuilder/blob/main/docs/core-concepts/users.mdx",1794580509317]