[{"data":1,"prerenderedAt":2355},["ShallowReactive",2],{"navigation":3,"page:\u002Fconnect":45},{"title":4,"links":5},"API reference",[6,9,12,15,18,21,24,27,30,33,36,39,42],{"title":7,"href":8},"Accounts","\u002Freference\u002Faccounts",{"title":10,"href":11},"Connect","\u002Freference\u002Fconnect",{"title":13,"href":14},"Calendars","\u002Freference\u002Fcalendars",{"title":16,"href":17},"Events","\u002Freference\u002Fevents",{"title":19,"href":20},"Availability","\u002Freference\u002Favailability",{"title":22,"href":23},"Meetings","\u002Freference\u002Fmeetings",{"title":25,"href":26},"Invitations","\u002Freference\u002Finvitations",{"title":28,"href":29},"Webhooks","\u002Freference\u002Fwebhooks",{"title":31,"href":32},"Metadata","\u002Freference\u002Fmetadata",{"title":34,"href":35},"Keys","\u002Freference\u002Fkeys",{"title":37,"href":38},"Sandbox","\u002Freference\u002Fsandbox",{"title":40,"href":41},"Usage","\u002Freference\u002Fusage",{"title":43,"href":44},"Logs","\u002Freference\u002Flogs",{"kind":46,"title":47,"description":48,"sections":49,"doc":68},"guide","Connecting accounts","Let your users connect their Google Calendar, Microsoft Outlook and Zoom accounts, with PyasConnect or with your own screens.",[50,53,56,59,62,65],{"id":51,"title":52},"pyas-auth-or-native-auth","Pyas Auth or Native Auth",{"id":54,"title":55},"additional-scopes","Additional scopes",{"id":57,"title":58},"option-1-pyasconnect","Option 1: PyasConnect",{"id":60,"title":61},"option-2-your-own-screens","Option 2: Your own screens",{"id":63,"title":64},"save-the-account-id","Save the account id",{"id":66,"title":67},"when-a-connection-breaks","When a connection breaks",{"id":69,"title":47,"body":70,"description":48,"extension":2346,"hero":2347,"meta":2348,"navigation":358,"path":2349,"rawbody":2350,"sections":2351,"seo":2352,"stem":432,"v1":2353,"__hash__":2354},"docs\u002Fconnect.md",{"type":71,"value":72,"toc":2331},"minimark",[73,77,84,138,141,144,152,170,172,175,203,267,290,296,298,305,312,317,320,475,485,527,534,538,544,1036,1039,1115,1121,1145,1167,1202,1204,1207,1213,1229,1233,1239,1378,1474,1551,1555,1564,1572,1575,1579,1582,1733,1886,1901,1904,1927,1930,1932,1940,1988,2000,2002,2005,2017,2024,2074,2085,2097,2107,2120,2296,2311,2327],[74,75,47],"h1",{"id":76},"connecting-accounts",[78,79,80],"lead",{},[81,82,83],"p",{},"Before you can read or write a user's calendar, the user connects their Google, Microsoft Outlook or Zoom account to your app. You can set this up in two ways. You can add PyasConnect, a ready-made sign-in dialog, to your page, or you can build your own screens on the API.",[85,86,87,102],"table",{},[88,89,90],"thead",{},[91,92,93,96,99],"tr",{},[94,95],"th",{},[94,97,98],{},"PyasConnect",[94,100,101],{},"Your own screens",[103,104,105,117,128],"tbody",{},[91,106,107,111,114],{},[108,109,110],"td",{},"What you build",[108,112,113],{},"One endpoint on your server, and the component on your page",[108,115,116],{},"The provider buttons, the redirects, and two requests from your server",[91,118,119,122,125],{},[108,120,121],{},"Requests from your server",[108,123,124],{},"One, from your endpoint",[108,126,127],{},"Two, one to get a sign-in URL and one to send the code",[91,129,130,133,136],{},[108,131,132],{},"Works with",[108,134,135],{},"Pyas Auth and Native Auth",[108,137,135],{},[81,139,140],{},"With either option, your API key stays on your server, and only your server uses it. To connect real accounts, you need a live key. A test key works only in the sandbox, which already has its own accounts.",[142,143,52],"h2",{"id":51},[81,145,146,147,151],{},"For each provider, you choose how people sign in. You set this in the dashboard, under your app's ",[148,149,150],"strong",{},"Provider setup",".",[153,154,155,162],"ul",{},[156,157,158,161],"li",{},[148,159,160],{},"Pyas Auth"," is the default. People sign in through Pyas's own OAuth app for Google, Microsoft or Zoom, so you don't need to set anything up. The provider's consent screen shows the Pyas name.",[156,163,164,167,168,151],{},[148,165,166],{},"Native Auth"," uses your own OAuth app for Google, Microsoft or Zoom. The provider's consent screen shows your app's name. To use it, enter your OAuth app's client ID, its client secret and your callback URL under ",[148,169,150],{},[142,171,55],{"id":54},[81,173,174],{},"By default, a connection asks for the access most apps need: reading and writing events, reading calendars, free\u002Fbusy and settings. Some features need more access, so the user is asked for it only when your app requests the feature. There are two ways to request them:",[153,176,177,189],{},[156,178,179,182,183,185,186,188],{},[148,180,181],{},"In the dashboard,"," under your app's ",[148,184,150],{},", in ",[148,187,55],{},". Choose them once for each provider, and every connection from your app asks for them, in PyasConnect and on your own screens.",[156,190,191,194,195,199,200,202],{},[148,192,193],{},"In a request,"," with ",[196,197,198],"code",{},"features"," when you create a connect session or a sign-in URL. A request that includes ",[196,201,198],{}," uses those instead of the dashboard's choice.",[85,204,205,215],{},[88,206,207],{},[91,208,209,212],{},[94,210,211],{},"Feature",[94,213,214],{},"What it adds",[103,216,217,227,237,247,257],{},[91,218,219,224],{},[108,220,221],{},[196,222,223],{},"manageCalendars",[108,225,226],{},"Create, rename, recolor and delete calendars, and add calendars to the person's list or remove them.",[91,228,229,234],{},[108,230,231],{},[196,232,233],{},"sharing",[108,235,236],{},"Share a calendar with other people and change their access.",[91,238,239,244],{},[108,240,241],{},[196,242,243],{},"meetingSettings",[108,245,246],{},"Change Google Meet and Microsoft Teams settings, such as who can join without waiting.",[91,248,249,254],{},[108,250,251],{},[196,252,253],{},"attachmentUploads",[108,255,256],{},"Upload files to events.",[91,258,259,264],{},[108,260,261],{},[196,262,263],{},"rooms",[108,265,266],{},"List meeting rooms and book them for events.",[81,268,269,270,272,273,272,275,272,277,279,280,282,283,285,286,289],{},"On Google, ",[196,271,223],{},", ",[196,274,233],{},[196,276,243],{},[196,278,263],{}," and ",[196,281,253],{}," need Native Auth for now. Google must approve this access for Pyas's own Google app first. If your app uses Pyas Auth for Google and requests one of them in ",[196,284,198],{},", the request fails with ",[196,287,288],{},"CONNECTION_PROVIDER_NOT_CONFIGURED",". In the dashboard, they can't be chosen until then. With Native Auth, add each feature's scopes to your own Google app; the dashboard lists them, and any Google API to turn on.",[81,291,292,293,295],{},"On Microsoft, every feature works with either option. For ",[196,294,263],{},", the user's Microsoft 365 administrator must approve the access, so people whose organization hasn't approved it can't connect while you request it.",[142,297,58],{"id":57},[81,299,300,301,304],{},"PyasConnect is a sign-in dialog that you add to your page. It shows the provider buttons, handles the sign-in, blocked pop-ups and errors, and saves the connected account. Before you start, add your site to your app's ",[148,302,303],{},"allowed origins"," in the dashboard. PyasConnect works only on those origins.",[81,306,307,308,311],{},"To try it first, open ",[148,309,310],{},"Lab"," in the dashboard. Pick your app, change how PyasConnect looks and behaves, and connect a real account in the preview, with no server code. The Lab then gives you the code for Vue, React, Angular or plain HTML, already filled in for your app.",[313,314,316],"h3",{"id":315},"_1-add-an-endpoint-to-your-server","1. Add an endpoint to your server",[81,318,319],{},"PyasConnect calls this endpoint each time a user opens the dialog. With the SDK, the endpoint is one line of code:",[321,322,323,439],"code-group",{},[324,325,331],"pre",{"className":326,"code":327,"filename":328,"language":329,"meta":330,"style":330},"language-js shiki shiki-themes css-variables","import { Pyas } from '@pyas\u002Fsdk'\n\nconst pyas = new Pyas({ apiKey: process.env.PYAS_API_KEY })\n\napp.post('\u002Fapi\u002Fpyas-session', pyas.connect.sessionHandler())\n","Express","js","",[196,332,333,353,360,402,407],{"__ignoreMap":330},[334,335,338,342,346,349],"span",{"class":336,"line":337},"line",1,[334,339,341],{"class":340},"sag7P","import",[334,343,345],{"class":344},"sqSWz"," { Pyas } ",[334,347,348],{"class":340},"from",[334,350,352],{"class":351},"sk1Au"," '@pyas\u002Fsdk'\n",[334,354,356],{"class":336,"line":355},2,[334,357,359],{"emptyLinePlaceholder":358},true,"\n",[334,361,363,366,370,373,376,380,383,386,389,391,394,396,399],{"class":336,"line":362},3,[334,364,365],{"class":340},"const",[334,367,369],{"class":368},"sXULy"," pyas",[334,371,372],{"class":340}," =",[334,374,375],{"class":340}," new",[334,377,379],{"class":378},"s1IGz"," Pyas",[334,381,382],{"class":344},"({ apiKey",[334,384,385],{"class":340},":",[334,387,388],{"class":368}," process",[334,390,151],{"class":344},[334,392,393],{"class":368},"env",[334,395,151],{"class":344},[334,397,398],{"class":368},"PYAS_API_KEY",[334,400,401],{"class":344}," })\n",[334,403,405],{"class":336,"line":404},4,[334,406,359],{"emptyLinePlaceholder":358},[334,408,410,413,416,419,422,426,428,430,433,436],{"class":336,"line":409},5,[334,411,412],{"class":368},"app",[334,414,415],{"class":378},".post",[334,417,418],{"class":344},"(",[334,420,421],{"class":351},"'\u002Fapi\u002Fpyas-session'",[334,423,425],{"class":424},"sEEt9",",",[334,427,369],{"class":368},[334,429,151],{"class":378},[334,431,432],{"class":368},"connect",[334,434,435],{"class":378},".sessionHandler",[334,437,438],{"class":344},"())\n",[324,440,443],{"className":326,"code":441,"filename":442,"language":329,"meta":330,"style":330},"\u002F\u002F app\u002Fapi\u002Fpyas-session\u002Froute.js\nexport const POST = pyas.connect.sessionHandler()\n","Next.js",[196,444,445,451],{"__ignoreMap":330},[334,446,447],{"class":336,"line":337},[334,448,450],{"class":449},"ssgCD","\u002F\u002F app\u002Fapi\u002Fpyas-session\u002Froute.js\n",[334,452,453,456,459,462,464,466,468,470,472],{"class":336,"line":355},[334,454,455],{"class":340},"export",[334,457,458],{"class":340}," const",[334,460,461],{"class":368}," POST",[334,463,372],{"class":340},[334,465,369],{"class":368},[334,467,151],{"class":378},[334,469,432],{"class":368},[334,471,435],{"class":378},[334,473,474],{"class":344},"()\n",[81,476,477,480,481,484],{},[148,478,479],{},"Optional: Save your own user id on the account."," If your app has its own id for each user, pass a function that returns the id of the user who is connecting. Pyas saves the id on the account as ",[196,482,483],{},"subjectId",", so you can find that user's accounts later.",[324,486,488],{"className":326,"code":487,"language":329,"meta":330,"style":330},"app.post('\u002Fapi\u002Fpyas-session', pyas.connect.sessionHandler((req) => req.user.id))\n",[196,489,490],{"__ignoreMap":330},[334,491,492,494,496,498,500,502,504,506,508,510,513,516,519,521,524],{"class":336,"line":337},[334,493,412],{"class":368},[334,495,415],{"class":378},[334,497,418],{"class":344},[334,499,421],{"class":351},[334,501,425],{"class":424},[334,503,369],{"class":368},[334,505,151],{"class":378},[334,507,432],{"class":368},[334,509,435],{"class":378},[334,511,512],{"class":344},"((req) ",[334,514,515],{"class":340},"=>",[334,517,518],{"class":368}," req",[334,520,151],{"class":344},[334,522,523],{"class":368},"user",[334,525,526],{"class":344},".id))\n",[81,528,529,530,533],{},"Replace ",[196,531,532],{},"req.user.id"," with wherever your app stores the id of the signed-in user.",[313,535,537],{"id":536},"_2-add-the-component-to-your-page","2. Add the component to your page",[81,539,540,541,385],{},"Give the component a function that calls your endpoint and returns the session token. When an account is connected, save its ",[196,542,543],{},"accountId",[321,545,546,678,852],{},[324,547,552],{"className":548,"code":549,"filename":550,"language":551,"meta":330,"style":330},"language-jsx shiki shiki-themes css-variables","import { PyasConnect } from '@pyas\u002Fconnect-react'\n\nconst getSessionToken = () =>\n  fetch('\u002Fapi\u002Fpyas-session', { method: 'POST' }).then((r) => r.json()).then((s) => s.token)\n\n\u003CPyasConnect getSessionToken={getSessionToken} onConnected={(account) => save(account.accountId)} \u002F>\n","React","jsx",[196,553,554,566,570,585,637,641],{"__ignoreMap":330},[334,555,556,558,561,563],{"class":336,"line":337},[334,557,341],{"class":340},[334,559,560],{"class":344}," { PyasConnect } ",[334,562,348],{"class":340},[334,564,565],{"class":351}," '@pyas\u002Fconnect-react'\n",[334,567,568],{"class":336,"line":355},[334,569,359],{"emptyLinePlaceholder":358},[334,571,572,574,577,579,582],{"class":336,"line":362},[334,573,365],{"class":340},[334,575,576],{"class":378}," getSessionToken",[334,578,372],{"class":340},[334,580,581],{"class":344}," () ",[334,583,584],{"class":340},"=>\n",[334,586,587,590,592,594,596,599,601,604,607,610,613,615,618,621,624,626,629,631,634],{"class":336,"line":404},[334,588,589],{"class":378},"  fetch",[334,591,418],{"class":344},[334,593,421],{"class":351},[334,595,425],{"class":424},[334,597,598],{"class":344}," { method",[334,600,385],{"class":340},[334,602,603],{"class":351}," 'POST'",[334,605,606],{"class":344}," })",[334,608,609],{"class":378},".then",[334,611,612],{"class":344},"((r) ",[334,614,515],{"class":340},[334,616,617],{"class":368}," r",[334,619,620],{"class":378},".json",[334,622,623],{"class":344},"())",[334,625,609],{"class":378},[334,627,628],{"class":344},"((s) ",[334,630,515],{"class":340},[334,632,633],{"class":368}," s",[334,635,636],{"class":344},".token)\n",[334,638,639],{"class":336,"line":409},[334,640,359],{"emptyLinePlaceholder":358},[334,642,644,647,649,651,654,657,660,662,665,667,670,672,675],{"class":336,"line":643},6,[334,645,646],{"class":344},"\u003C",[334,648,98],{"class":368},[334,650,576],{"class":378},[334,652,653],{"class":340},"=",[334,655,656],{"class":344},"{getSessionToken} ",[334,658,659],{"class":378},"onConnected",[334,661,653],{"class":340},[334,663,664],{"class":344},"{(account) ",[334,666,515],{"class":340},[334,668,669],{"class":378}," save",[334,671,418],{"class":344},[334,673,674],{"class":368},"account",[334,676,677],{"class":344},".accountId)} \u002F>\n",[324,679,684],{"className":680,"code":681,"filename":682,"language":683,"meta":330,"style":330},"language-vue shiki shiki-themes css-variables","\u003Cscript setup>\nimport { PyasConnect } from '@pyas\u002Fconnect-vue'\n\nconst getSessionToken = () =>\n  fetch('\u002Fapi\u002Fpyas-session', { method: 'POST' }).then((r) => r.json()).then((s) => s.token)\n\u003C\u002Fscript>\n\n\u003Ctemplate>\n  \u003CPyasConnect :get-session-token=\"getSessionToken\" @connected=\"(account) => save(account.accountId)\" \u002F>\n\u003C\u002Ftemplate>\n","Vue","vue",[196,685,686,699,710,714,726,766,775,780,790,843],{"__ignoreMap":330},[334,687,688,690,693,696],{"class":336,"line":337},[334,689,646],{"class":344},[334,691,692],{"class":351},"script",[334,694,695],{"class":378}," setup",[334,697,698],{"class":344},">\n",[334,700,701,703,705,707],{"class":336,"line":355},[334,702,341],{"class":340},[334,704,560],{"class":344},[334,706,348],{"class":340},[334,708,709],{"class":351}," '@pyas\u002Fconnect-vue'\n",[334,711,712],{"class":336,"line":362},[334,713,359],{"emptyLinePlaceholder":358},[334,715,716,718,720,722,724],{"class":336,"line":404},[334,717,365],{"class":340},[334,719,576],{"class":378},[334,721,372],{"class":340},[334,723,581],{"class":344},[334,725,584],{"class":340},[334,727,728,730,732,734,736,738,740,742,744,746,748,750,752,754,756,758,760,762,764],{"class":336,"line":409},[334,729,589],{"class":378},[334,731,418],{"class":344},[334,733,421],{"class":351},[334,735,425],{"class":424},[334,737,598],{"class":344},[334,739,385],{"class":340},[334,741,603],{"class":351},[334,743,606],{"class":344},[334,745,609],{"class":378},[334,747,612],{"class":344},[334,749,515],{"class":340},[334,751,617],{"class":368},[334,753,620],{"class":378},[334,755,623],{"class":344},[334,757,609],{"class":378},[334,759,628],{"class":344},[334,761,515],{"class":340},[334,763,633],{"class":368},[334,765,636],{"class":344},[334,767,768,771,773],{"class":336,"line":643},[334,769,770],{"class":344},"\u003C\u002F",[334,772,692],{"class":351},[334,774,698],{"class":344},[334,776,778],{"class":336,"line":777},7,[334,779,359],{"emptyLinePlaceholder":358},[334,781,783,785,788],{"class":336,"line":782},8,[334,784,646],{"class":344},[334,786,787],{"class":351},"template",[334,789,698],{"class":344},[334,791,793,796,798,801,804,806,809,812,814,817,820,822,824,827,829,831,833,835,838,840],{"class":336,"line":792},9,[334,794,795],{"class":344},"  \u003C",[334,797,98],{"class":351},[334,799,800],{"class":344}," :",[334,802,803],{"class":378},"get-session-token",[334,805,653],{"class":340},[334,807,808],{"class":344},"\"",[334,810,811],{"class":344},"getSessionToken",[334,813,808],{"class":344},[334,815,816],{"class":344}," @",[334,818,819],{"class":378},"connected",[334,821,653],{"class":340},[334,823,808],{"class":344},[334,825,826],{"class":344},"(account) ",[334,828,515],{"class":340},[334,830,669],{"class":378},[334,832,418],{"class":344},[334,834,674],{"class":368},[334,836,837],{"class":344},".accountId)",[334,839,808],{"class":344},[334,841,842],{"class":344}," \u002F>\n",[334,844,846,848,850],{"class":336,"line":845},10,[334,847,770],{"class":344},[334,849,787],{"class":351},[334,851,698],{"class":344},[324,853,858],{"className":854,"code":855,"filename":856,"language":857,"meta":330,"style":330},"language-html shiki shiki-themes css-variables","\u003Cscript type=\"module\" src=\"https:\u002F\u002Fcdn.jsdelivr.net\u002Fnpm\u002F@pyas\u002Fconnect@2\u002Fdist\u002Fpyas-connect\u002Fpyas-connect.esm.js\">\u003C\u002Fscript>\n\n\u003Cpyas-connect>\u003C\u002Fpyas-connect>\n\u003Cscript>\n  const connect = document.querySelector('pyas-connect')\n  connect.getSessionToken = () =>\n    fetch('\u002Fapi\u002Fpyas-session', { method: 'POST' }).then((r) => r.json()).then((s) => s.token)\n  connect.addEventListener('connected', (e) => save(e.detail.accountId))\n\u003C\u002Fscript>\n","HTML","html",[196,859,860,889,893,906,914,938,953,994,1028],{"__ignoreMap":330},[334,861,862,864,866,869,871,874,877,879,882,885,887],{"class":336,"line":337},[334,863,646],{"class":344},[334,865,692],{"class":351},[334,867,868],{"class":378}," type",[334,870,653],{"class":340},[334,872,873],{"class":351},"\"module\"",[334,875,876],{"class":378}," src",[334,878,653],{"class":340},[334,880,881],{"class":351},"\"https:\u002F\u002Fcdn.jsdelivr.net\u002Fnpm\u002F@pyas\u002Fconnect@2\u002Fdist\u002Fpyas-connect\u002Fpyas-connect.esm.js\"",[334,883,884],{"class":344},">\u003C\u002F",[334,886,692],{"class":351},[334,888,698],{"class":344},[334,890,891],{"class":336,"line":355},[334,892,359],{"emptyLinePlaceholder":358},[334,894,895,897,900,902,904],{"class":336,"line":362},[334,896,646],{"class":344},[334,898,899],{"class":351},"pyas-connect",[334,901,884],{"class":344},[334,903,899],{"class":351},[334,905,698],{"class":344},[334,907,908,910,912],{"class":336,"line":404},[334,909,646],{"class":344},[334,911,692],{"class":351},[334,913,698],{"class":344},[334,915,916,919,922,924,927,930,932,935],{"class":336,"line":409},[334,917,918],{"class":340},"  const",[334,920,921],{"class":368}," connect",[334,923,372],{"class":340},[334,925,926],{"class":368}," document",[334,928,929],{"class":378},".querySelector",[334,931,418],{"class":344},[334,933,934],{"class":351},"'pyas-connect'",[334,936,937],{"class":344},")\n",[334,939,940,943,945,947,949,951],{"class":336,"line":643},[334,941,942],{"class":368},"  connect",[334,944,151],{"class":344},[334,946,811],{"class":378},[334,948,372],{"class":340},[334,950,581],{"class":344},[334,952,584],{"class":340},[334,954,955,958,960,962,964,966,968,970,972,974,976,978,980,982,984,986,988,990,992],{"class":336,"line":777},[334,956,957],{"class":378},"    fetch",[334,959,418],{"class":344},[334,961,421],{"class":351},[334,963,425],{"class":424},[334,965,598],{"class":344},[334,967,385],{"class":340},[334,969,603],{"class":351},[334,971,606],{"class":344},[334,973,609],{"class":378},[334,975,612],{"class":344},[334,977,515],{"class":340},[334,979,617],{"class":368},[334,981,620],{"class":378},[334,983,623],{"class":344},[334,985,609],{"class":378},[334,987,628],{"class":344},[334,989,515],{"class":340},[334,991,633],{"class":368},[334,993,636],{"class":344},[334,995,996,998,1001,1003,1006,1008,1011,1013,1015,1017,1020,1022,1025],{"class":336,"line":782},[334,997,942],{"class":368},[334,999,1000],{"class":378},".addEventListener",[334,1002,418],{"class":344},[334,1004,1005],{"class":351},"'connected'",[334,1007,425],{"class":424},[334,1009,1010],{"class":344}," (e) ",[334,1012,515],{"class":340},[334,1014,669],{"class":378},[334,1016,418],{"class":344},[334,1018,1019],{"class":368},"e",[334,1021,151],{"class":344},[334,1023,1024],{"class":368},"detail",[334,1026,1027],{"class":344},".accountId))\n",[334,1029,1030,1032,1034],{"class":336,"line":792},[334,1031,770],{"class":344},[334,1033,692],{"class":351},[334,1035,698],{"class":344},[81,1037,1038],{},"PyasConnect shows a \"Connect an account\" button, with your app's name and icon from the dashboard. When a user connects an account, your handler receives the account:",[324,1040,1044],{"className":1041,"code":1042,"language":1043,"meta":330,"style":330},"language-json shiki shiki-themes css-variables","{\n  \"accountId\": \"6b0e3f1a-8c2d-4e5f-9a7b-1c3d5e7f9a2b\",\n  \"provider\": \"google\",\n  \"email\": \"avery@yourcustomer.com\",\n  \"name\": \"Avery Brooks\",\n  \"subjectId\": \"user_42\"\n}\n","json",[196,1045,1046,1051,1064,1076,1088,1100,1110],{"__ignoreMap":330},[334,1047,1048],{"class":336,"line":337},[334,1049,1050],{"class":344},"{\n",[334,1052,1053,1056,1058,1061],{"class":336,"line":355},[334,1054,1055],{"class":340},"  \"accountId\"",[334,1057,385],{"class":424},[334,1059,1060],{"class":351}," \"6b0e3f1a-8c2d-4e5f-9a7b-1c3d5e7f9a2b\"",[334,1062,1063],{"class":424},",\n",[334,1065,1066,1069,1071,1074],{"class":336,"line":362},[334,1067,1068],{"class":340},"  \"provider\"",[334,1070,385],{"class":424},[334,1072,1073],{"class":351}," \"google\"",[334,1075,1063],{"class":424},[334,1077,1078,1081,1083,1086],{"class":336,"line":404},[334,1079,1080],{"class":340},"  \"email\"",[334,1082,385],{"class":424},[334,1084,1085],{"class":351}," \"avery@yourcustomer.com\"",[334,1087,1063],{"class":424},[334,1089,1090,1093,1095,1098],{"class":336,"line":409},[334,1091,1092],{"class":340},"  \"name\"",[334,1094,385],{"class":424},[334,1096,1097],{"class":351}," \"Avery Brooks\"",[334,1099,1063],{"class":424},[334,1101,1102,1105,1107],{"class":336,"line":643},[334,1103,1104],{"class":340},"  \"subjectId\"",[334,1106,385],{"class":424},[334,1108,1109],{"class":351}," \"user_42\"\n",[334,1111,1112],{"class":336,"line":777},[334,1113,1114],{"class":344},"}\n",[81,1116,1117,1118,1120],{},"With ",[148,1119,166],{},", the provider redirects people to your callback URL instead of to Pyas. Add this code to your callback page so that it passes the result to the dialog:",[324,1122,1124],{"className":326,"code":1123,"language":329,"meta":330,"style":330},"import { postCallbackResult } from '@pyas\u002Fconnect'\npostCallbackResult()\n",[196,1125,1126,1138],{"__ignoreMap":330},[334,1127,1128,1130,1133,1135],{"class":336,"line":337},[334,1129,341],{"class":340},[334,1131,1132],{"class":344}," { postCallbackResult } ",[334,1134,348],{"class":340},[334,1136,1137],{"class":351}," '@pyas\u002Fconnect'\n",[334,1139,1140,1143],{"class":336,"line":355},[334,1141,1142],{"class":378},"postCallbackResult",[334,1144,474],{"class":344},[81,1146,1147,1148,1151,1152,1155,1156,1159,1160,1163,1164,151],{},"The dialog is available in English, Norwegian, Swedish, Danish, German, French, Spanish, Dutch and Portuguese (Brazil). It uses your page's language (",[196,1149,1150],{},"\u003Chtml lang>","), then the browser's. To choose the language yourself, set ",[196,1153,1154],{},"locale",", for example ",[196,1157,1158],{},"locale=\"de\"",". The pages Pyas shows in redirect mode work the same way: they use the browser's language, or add ",[196,1161,1162],{},"&locale=de"," to the session's ",[196,1165,1166],{},"url",[81,1168,1169,1170,272,1179,272,1186,1193,1194,1201],{},"For themes, custom text, a custom button and every other option, see the package documentation on npm: ",[1171,1172,1176],"a",{"href":1173,"rel":1174},"https:\u002F\u002Fwww.npmjs.com\u002Fpackage\u002F@pyas\u002Fconnect-react",[1175],"nofollow",[196,1177,1178],{},"@pyas\u002Fconnect-react",[1171,1180,1183],{"href":1181,"rel":1182},"https:\u002F\u002Fwww.npmjs.com\u002Fpackage\u002F@pyas\u002Fconnect-vue",[1175],[196,1184,1185],{},"@pyas\u002Fconnect-vue",[1171,1187,1190],{"href":1188,"rel":1189},"https:\u002F\u002Fwww.npmjs.com\u002Fpackage\u002F@pyas\u002Fconnect-angular",[1175],[196,1191,1192],{},"@pyas\u002Fconnect-angular",", and ",[1171,1195,1198],{"href":1196,"rel":1197},"https:\u002F\u002Fwww.npmjs.com\u002Fpackage\u002F@pyas\u002Fconnect",[1175],[196,1199,1200],{},"@pyas\u002Fconnect"," for any other page.",[142,1203,61],{"id":60},[81,1205,1206],{},"With this option, you show the provider buttons yourself. When a user selects one, your server requests a sign-in URL from the API and redirects the user to it. After the user signs in, they return to your app with a code, and your server sends that code to the API to save the account.",[81,1208,1209,1210,1212],{},"First, set the page that users return to after they sign in. You do this under ",[148,1211,150],{}," in the dashboard:",[153,1214,1215,1224],{},[156,1216,1217,1219,1220,1223],{},[148,1218,160],{},": Set the provider's ",[148,1221,1222],{},"Redirect URL"," to the page in your app that receives the code. Pyas redirects people to that page after they sign in.",[156,1225,1226,1228],{},[148,1227,166],{},": Your callback URL is already set in Provider setup, and the provider redirects people to it.",[313,1230,1232],{"id":1231},"_1-get-a-sign-in-url","1. Get a sign-in URL",[81,1234,1235,1236,1238],{},"When the user selects a provider button, request a sign-in URL from your server, then redirect the user to the ",[196,1237,1166],{}," in the response:",[321,1240,1241,1350],{},[324,1242,1245],{"className":326,"code":1243,"filename":1244,"language":329,"meta":330,"style":330},"import { Pyas } from '@pyas\u002Fsdk'\n\nconst pyas = new Pyas({ apiKey: process.env.PYAS_API_KEY })\n\nconst { url } = await pyas.connect.oauth({ provider: 'google', state: 'user_42' })\nres.redirect(url)\n","Node.js",[196,1246,1247,1257,1261,1289,1293,1339],{"__ignoreMap":330},[334,1248,1249,1251,1253,1255],{"class":336,"line":337},[334,1250,341],{"class":340},[334,1252,345],{"class":344},[334,1254,348],{"class":340},[334,1256,352],{"class":351},[334,1258,1259],{"class":336,"line":355},[334,1260,359],{"emptyLinePlaceholder":358},[334,1262,1263,1265,1267,1269,1271,1273,1275,1277,1279,1281,1283,1285,1287],{"class":336,"line":362},[334,1264,365],{"class":340},[334,1266,369],{"class":368},[334,1268,372],{"class":340},[334,1270,375],{"class":340},[334,1272,379],{"class":378},[334,1274,382],{"class":344},[334,1276,385],{"class":340},[334,1278,388],{"class":368},[334,1280,151],{"class":344},[334,1282,393],{"class":368},[334,1284,151],{"class":344},[334,1286,398],{"class":368},[334,1288,401],{"class":344},[334,1290,1291],{"class":336,"line":404},[334,1292,359],{"emptyLinePlaceholder":358},[334,1294,1295,1297,1300,1302,1305,1307,1310,1312,1314,1316,1319,1322,1324,1327,1329,1332,1334,1337],{"class":336,"line":409},[334,1296,365],{"class":340},[334,1298,1299],{"class":344}," { ",[334,1301,1166],{"class":368},[334,1303,1304],{"class":344}," } ",[334,1306,653],{"class":340},[334,1308,1309],{"class":340}," await",[334,1311,369],{"class":368},[334,1313,151],{"class":378},[334,1315,432],{"class":368},[334,1317,1318],{"class":378},".oauth",[334,1320,1321],{"class":344},"({ provider",[334,1323,385],{"class":340},[334,1325,1326],{"class":351}," 'google'",[334,1328,425],{"class":424},[334,1330,1331],{"class":344}," state",[334,1333,385],{"class":340},[334,1335,1336],{"class":351}," 'user_42'",[334,1338,401],{"class":344},[334,1340,1341,1344,1347],{"class":336,"line":643},[334,1342,1343],{"class":368},"res",[334,1345,1346],{"class":378},".redirect",[334,1348,1349],{"class":344},"(url)\n",[324,1351,1356],{"className":1352,"code":1353,"filename":1354,"language":1355,"meta":330,"style":330},"language-bash shiki shiki-themes css-variables","curl \"https:\u002F\u002Fapi.pyas.io\u002Fv2\u002Fconnect\u002Foauth?provider=google&state=user_42\" \\\n  -H \"x-api-key: $PYAS_API_KEY\"\n","cURL","bash",[196,1357,1358,1369],{"__ignoreMap":330},[334,1359,1360,1363,1366],{"class":336,"line":337},[334,1361,1362],{"class":378},"curl",[334,1364,1365],{"class":351}," \"https:\u002F\u002Fapi.pyas.io\u002Fv2\u002Fconnect\u002Foauth?provider=google&state=user_42\"",[334,1367,1368],{"class":344}," \\\n",[334,1370,1371,1375],{"class":336,"line":355},[334,1372,1374],{"class":1373},"sTB4W","  -H",[334,1376,1377],{"class":351}," \"x-api-key: $PYAS_API_KEY\"\n",[324,1379,1381],{"className":1041,"code":1380,"language":1043,"meta":330,"style":330},"{\n  \"success\": true,\n  \"data\": {\n    \"provider\": \"google\",\n    \"url\": \"https:\u002F\u002Faccounts.google.com\u002Fo\u002Foauth2\u002Fv2\u002Fauth?client_id=...&redirect_uri=...&response_type=code&scope=...&state=...\"\n  },\n  \"provider\": null,\n  \"traceId\": \"trc_5f2a9c1e7b3d4a6f8e0c2b1d\",\n  \"meta\": {}\n}\n",[196,1382,1383,1387,1399,1409,1420,1430,1437,1448,1460,1470],{"__ignoreMap":330},[334,1384,1385],{"class":336,"line":337},[334,1386,1050],{"class":344},[334,1388,1389,1392,1394,1397],{"class":336,"line":355},[334,1390,1391],{"class":340},"  \"success\"",[334,1393,385],{"class":424},[334,1395,1396],{"class":368}," true",[334,1398,1063],{"class":424},[334,1400,1401,1404,1406],{"class":336,"line":362},[334,1402,1403],{"class":340},"  \"data\"",[334,1405,385],{"class":424},[334,1407,1408],{"class":344}," {\n",[334,1410,1411,1414,1416,1418],{"class":336,"line":404},[334,1412,1413],{"class":340},"    \"provider\"",[334,1415,385],{"class":424},[334,1417,1073],{"class":351},[334,1419,1063],{"class":424},[334,1421,1422,1425,1427],{"class":336,"line":409},[334,1423,1424],{"class":340},"    \"url\"",[334,1426,385],{"class":424},[334,1428,1429],{"class":351}," \"https:\u002F\u002Faccounts.google.com\u002Fo\u002Foauth2\u002Fv2\u002Fauth?client_id=...&redirect_uri=...&response_type=code&scope=...&state=...\"\n",[334,1431,1432,1435],{"class":336,"line":643},[334,1433,1434],{"class":344},"  }",[334,1436,1063],{"class":424},[334,1438,1439,1441,1443,1446],{"class":336,"line":777},[334,1440,1068],{"class":340},[334,1442,385],{"class":424},[334,1444,1445],{"class":368}," null",[334,1447,1063],{"class":424},[334,1449,1450,1453,1455,1458],{"class":336,"line":782},[334,1451,1452],{"class":340},"  \"traceId\"",[334,1454,385],{"class":424},[334,1456,1457],{"class":351}," \"trc_5f2a9c1e7b3d4a6f8e0c2b1d\"",[334,1459,1063],{"class":424},[334,1461,1462,1465,1467],{"class":336,"line":792},[334,1463,1464],{"class":340},"  \"meta\"",[334,1466,385],{"class":424},[334,1468,1469],{"class":344}," {}\n",[334,1471,1472],{"class":336,"line":845},[334,1473,1114],{"class":344},[85,1475,1476,1486],{},[88,1477,1478],{},[91,1479,1480,1483],{},[94,1481,1482],{},"Parameter",[94,1484,1485],{},"Description",[103,1487,1488,1508,1518,1528],{},[91,1489,1490,1495],{},[108,1491,1492],{},[196,1493,1494],{},"provider",[108,1496,1497,272,1500,1503,1504,1507],{},[196,1498,1499],{},"google",[196,1501,1502],{},"microsoft"," or ",[196,1505,1506],{},"zoom",". Required.",[91,1509,1510,1515],{},[108,1511,1512],{},[196,1513,1514],{},"state",[108,1516,1517],{},"Any value you choose, such as your id for the user. It's returned to you with the code.",[91,1519,1520,1525],{},[108,1521,1522],{},[196,1523,1524],{},"loginHint",[108,1526,1527],{},"An email address to prefill on the provider's sign-in page.",[91,1529,1530,1534],{},[108,1531,1532],{},[196,1533,198],{},[108,1535,1536,1537,272,1539,272,1541,272,1543,279,1545,1547,1548,151],{},"Additional access to request, as a comma-separated list of ",[196,1538,223],{},[196,1540,233],{},[196,1542,243],{},[196,1544,253],{},[196,1546,263],{},". See ",[1171,1549,55],{"href":1550},"#additional-scopes",[313,1552,1554],{"id":1553},"_2-the-user-returns-with-a-code","2. The user returns with a code",[81,1556,1557,1558,1560,1561,1563],{},"After signing in, the user is redirected to your Redirect URL (Pyas Auth) or your callback URL (Native Auth). The URL includes ",[196,1559,196],{}," and your ",[196,1562,1514],{}," as query parameters:",[324,1565,1570],{"className":1566,"code":1568,"language":1569},[1567],"language-text","https:\u002F\u002Fyourapp.com\u002Fconnected\u002Fgoogle?code=4\u002F0AeanS0Z...&state=user_42\n","text",[196,1571,1568],{"__ignoreMap":330},[81,1573,1574],{},"If the user cancels or the provider denies access, there's no valid code, and the request in step 3 returns an error. Show your own message and let the user try again.",[313,1576,1578],{"id":1577},"_3-exchange-the-code-for-an-account","3. Exchange the code for an account",[81,1580,1581],{},"From your server, send the code to the API. The API saves the account and returns it:",[321,1583,1584,1688],{},[324,1585,1587],{"className":326,"code":1586,"filename":1244,"language":329,"meta":330,"style":330},"const account = await pyas.connect.accounts.create({\n  provider: 'google',\n  code: req.query.code,\n  subjectId: req.query.state,\n})\nawait saveAccount(user, account.accountId)\n",[196,1588,1589,1617,1628,1647,1665,1670],{"__ignoreMap":330},[334,1590,1591,1593,1596,1598,1600,1602,1604,1606,1608,1611,1614],{"class":336,"line":337},[334,1592,365],{"class":340},[334,1594,1595],{"class":368}," account",[334,1597,372],{"class":340},[334,1599,1309],{"class":340},[334,1601,369],{"class":368},[334,1603,151],{"class":378},[334,1605,432],{"class":368},[334,1607,151],{"class":378},[334,1609,1610],{"class":368},"accounts",[334,1612,1613],{"class":378},".create",[334,1615,1616],{"class":344},"({\n",[334,1618,1619,1622,1624,1626],{"class":336,"line":355},[334,1620,1621],{"class":344},"  provider",[334,1623,385],{"class":340},[334,1625,1326],{"class":351},[334,1627,1063],{"class":424},[334,1629,1630,1633,1635,1637,1639,1642,1645],{"class":336,"line":362},[334,1631,1632],{"class":344},"  code",[334,1634,385],{"class":340},[334,1636,518],{"class":368},[334,1638,151],{"class":344},[334,1640,1641],{"class":368},"query",[334,1643,1644],{"class":344},".code",[334,1646,1063],{"class":424},[334,1648,1649,1652,1654,1656,1658,1660,1663],{"class":336,"line":404},[334,1650,1651],{"class":344},"  subjectId",[334,1653,385],{"class":340},[334,1655,518],{"class":368},[334,1657,151],{"class":344},[334,1659,1641],{"class":368},[334,1661,1662],{"class":344},".state",[334,1664,1063],{"class":424},[334,1666,1667],{"class":336,"line":409},[334,1668,1669],{"class":344},"})\n",[334,1671,1672,1675,1678,1681,1683,1685],{"class":336,"line":643},[334,1673,1674],{"class":340},"await",[334,1676,1677],{"class":378}," saveAccount",[334,1679,1680],{"class":344},"(user",[334,1682,425],{"class":424},[334,1684,1595],{"class":368},[334,1686,1687],{"class":344},".accountId)\n",[324,1689,1691],{"className":1352,"code":1690,"filename":1354,"language":1355,"meta":330,"style":330},"curl -X POST https:\u002F\u002Fapi.pyas.io\u002Fv2\u002Fconnect\u002Faccounts \\\n  -H \"x-api-key: $PYAS_API_KEY\" \\\n  -H \"content-type: application\u002Fjson\" \\\n  -d '{ \"provider\": \"google\", \"code\": \"4\u002F0AeanS0Z...\", \"subjectId\": \"user_42\" }'\n",[196,1692,1693,1707,1716,1725],{"__ignoreMap":330},[334,1694,1695,1697,1700,1702,1705],{"class":336,"line":337},[334,1696,1362],{"class":378},[334,1698,1699],{"class":1373}," -X",[334,1701,461],{"class":1373},[334,1703,1704],{"class":1373}," https:\u002F\u002Fapi.pyas.io\u002Fv2\u002Fconnect\u002Faccounts",[334,1706,1368],{"class":344},[334,1708,1709,1711,1714],{"class":336,"line":355},[334,1710,1374],{"class":1373},[334,1712,1713],{"class":351}," \"x-api-key: $PYAS_API_KEY\"",[334,1715,1368],{"class":344},[334,1717,1718,1720,1723],{"class":336,"line":362},[334,1719,1374],{"class":1373},[334,1721,1722],{"class":351}," \"content-type: application\u002Fjson\"",[334,1724,1368],{"class":344},[334,1726,1727,1730],{"class":336,"line":404},[334,1728,1729],{"class":1373},"  -d",[334,1731,1732],{"class":351}," '{ \"provider\": \"google\", \"code\": \"4\u002F0AeanS0Z...\", \"subjectId\": \"user_42\" }'\n",[324,1734,1736],{"className":1041,"code":1735,"language":1043,"meta":330,"style":330},"{\n  \"success\": true,\n  \"data\": {\n    \"accountId\": \"6b0e3f1a-8c2d-4e5f-9a7b-1c3d5e7f9a2b\",\n    \"provider\": \"google\",\n    \"email\": \"avery@yourcustomer.com\",\n    \"name\": \"Avery Brooks\",\n    \"subjectId\": \"user_42\",\n    \"existing\": false,\n    \"grantedScopes\": [\"https:\u002F\u002Fwww.googleapis.com\u002Fauth\u002Fcalendar\"]\n  },\n  \"provider\": null,\n  \"traceId\": \"trc_5f2a9c1e7b3d4a6f8e0c2b1d\",\n  \"meta\": {}\n}\n",[196,1737,1738,1742,1752,1760,1771,1781,1792,1803,1815,1827,1843,1850,1861,1872,1881],{"__ignoreMap":330},[334,1739,1740],{"class":336,"line":337},[334,1741,1050],{"class":344},[334,1743,1744,1746,1748,1750],{"class":336,"line":355},[334,1745,1391],{"class":340},[334,1747,385],{"class":424},[334,1749,1396],{"class":368},[334,1751,1063],{"class":424},[334,1753,1754,1756,1758],{"class":336,"line":362},[334,1755,1403],{"class":340},[334,1757,385],{"class":424},[334,1759,1408],{"class":344},[334,1761,1762,1765,1767,1769],{"class":336,"line":404},[334,1763,1764],{"class":340},"    \"accountId\"",[334,1766,385],{"class":424},[334,1768,1060],{"class":351},[334,1770,1063],{"class":424},[334,1772,1773,1775,1777,1779],{"class":336,"line":409},[334,1774,1413],{"class":340},[334,1776,385],{"class":424},[334,1778,1073],{"class":351},[334,1780,1063],{"class":424},[334,1782,1783,1786,1788,1790],{"class":336,"line":643},[334,1784,1785],{"class":340},"    \"email\"",[334,1787,385],{"class":424},[334,1789,1085],{"class":351},[334,1791,1063],{"class":424},[334,1793,1794,1797,1799,1801],{"class":336,"line":777},[334,1795,1796],{"class":340},"    \"name\"",[334,1798,385],{"class":424},[334,1800,1097],{"class":351},[334,1802,1063],{"class":424},[334,1804,1805,1808,1810,1813],{"class":336,"line":782},[334,1806,1807],{"class":340},"    \"subjectId\"",[334,1809,385],{"class":424},[334,1811,1812],{"class":351}," \"user_42\"",[334,1814,1063],{"class":424},[334,1816,1817,1820,1822,1825],{"class":336,"line":792},[334,1818,1819],{"class":340},"    \"existing\"",[334,1821,385],{"class":424},[334,1823,1824],{"class":368}," false",[334,1826,1063],{"class":424},[334,1828,1829,1832,1834,1837,1840],{"class":336,"line":845},[334,1830,1831],{"class":340},"    \"grantedScopes\"",[334,1833,385],{"class":424},[334,1835,1836],{"class":344}," [",[334,1838,1839],{"class":351},"\"https:\u002F\u002Fwww.googleapis.com\u002Fauth\u002Fcalendar\"",[334,1841,1842],{"class":344},"]\n",[334,1844,1846,1848],{"class":336,"line":1845},11,[334,1847,1434],{"class":344},[334,1849,1063],{"class":424},[334,1851,1853,1855,1857,1859],{"class":336,"line":1852},12,[334,1854,1068],{"class":340},[334,1856,385],{"class":424},[334,1858,1445],{"class":368},[334,1860,1063],{"class":424},[334,1862,1864,1866,1868,1870],{"class":336,"line":1863},13,[334,1865,1452],{"class":340},[334,1867,385],{"class":424},[334,1869,1457],{"class":351},[334,1871,1063],{"class":424},[334,1873,1875,1877,1879],{"class":336,"line":1874},14,[334,1876,1464],{"class":340},[334,1878,385],{"class":424},[334,1880,1469],{"class":344},[334,1882,1884],{"class":336,"line":1883},15,[334,1885,1114],{"class":344},[81,1887,1888,1890,1891,1503,1894,1547,1897,151],{},[196,1889,483],{}," is optional. It's your own id for the user, and the API saves it on the account. Each code can be used only once. If the request fails, the error explains why, for example with ",[196,1892,1893],{},"CONNECTION_CONSENT_FAILED",[196,1895,1896],{},"BILLING_CONNECTION_LIMIT",[1171,1898,1900],{"href":1899},"\u002Ferrors","Errors",[81,1902,1903],{},"If you're coming from v1, steps 1 and 3 replace these two v1 endpoints:",[324,1905,1909],{"className":1906,"code":1907,"language":1908,"meta":330,"style":330},"language-http shiki shiki-themes css-variables","GET \u002F{provider}\u002Foauth\nPOST \u002F{provider}\u002Fconnect\n","http",[196,1910,1911,1919],{"__ignoreMap":330},[334,1912,1913,1916],{"class":336,"line":337},[334,1914,1915],{"class":340},"GET",[334,1917,1918],{"class":344}," \u002F{provider}\u002Foauth\n",[334,1920,1921,1924],{"class":336,"line":355},[334,1922,1923],{"class":340},"POST",[334,1925,1926],{"class":344}," \u002F{provider}\u002Fconnect\n",[81,1928,1929],{},"In v2, the provider is a parameter instead of part of the path. The v1 endpoints still work.",[142,1931,64],{"id":63},[81,1933,1934,1935,1937,1938,385],{},"With either option, store the ",[196,1936,543],{}," with your user. Every request that reads or writes their calendar must include it. To find a user's accounts later, list them by the id you saved as ",[196,1939,483],{},[324,1941,1943],{"className":326,"code":1942,"language":329,"meta":330,"style":330},"for await (const account of pyas.accounts.list({ subjectId: user.id })) { \u002F* ... *\u002F }\n",[196,1944,1945],{"__ignoreMap":330},[334,1946,1947,1950,1952,1955,1957,1959,1962,1964,1966,1968,1971,1974,1976,1979,1982,1985],{"class":336,"line":337},[334,1948,1949],{"class":340},"for",[334,1951,1309],{"class":340},[334,1953,1954],{"class":344}," (",[334,1956,365],{"class":340},[334,1958,1595],{"class":368},[334,1960,1961],{"class":340}," of",[334,1963,369],{"class":368},[334,1965,151],{"class":378},[334,1967,1610],{"class":368},[334,1969,1970],{"class":378},".list",[334,1972,1973],{"class":344},"({ subjectId",[334,1975,385],{"class":340},[334,1977,1978],{"class":368}," user",[334,1980,1981],{"class":344},".id })) { ",[334,1983,1984],{"class":449},"\u002F* ... *\u002F",[334,1986,1987],{"class":344}," }\n",[81,1989,1990,1991,1994,1995,1999],{},"Pyas also sends a ",[196,1992,1993],{},"connection.created"," ",[1171,1996,1998],{"href":1997},"\u002Fwebhooks","webhook"," event for every new account.",[142,2001,67],{"id":66},[81,2003,2004],{},"A connection can stop working when a user revokes access, changes their password or leaves their company. To find the accounts that need attention, call:",[324,2006,2008],{"className":1906,"code":2007,"language":1908,"meta":330,"style":330},"GET \u002Fv2\u002Faccounts\u002Fstatus?overall=attention,broken\n",[196,2009,2010],{"__ignoreMap":330},[334,2011,2012,2014],{"class":336,"line":337},[334,2013,1915],{"class":340},[334,2015,2016],{"class":344}," \u002Fv2\u002Faccounts\u002Fstatus?overall=attention,broken\n",[81,2018,2019,2020,2023],{},"Each account in the response has an ",[196,2021,2022],{},"overall"," status:",[85,2025,2026,2038],{},[88,2027,2028],{},[91,2029,2030,2035],{},[94,2031,2032],{},[196,2033,2034],{},"overall.status",[94,2036,2037],{},"Meaning",[103,2039,2040,2050,2064],{},[91,2041,2042,2047],{},[108,2043,2044],{},[196,2045,2046],{},"healthy",[108,2048,2049],{},"The connection works.",[91,2051,2052,2057],{},[108,2053,2054],{},[196,2055,2056],{},"attention",[108,2058,2059,2060,2063],{},"The connection works, but some features are limited, for example because of a missing permission. ",[196,2061,2062],{},"reason"," explains the limitation.",[91,2065,2066,2071],{},[108,2067,2068],{},[196,2069,2070],{},"broken",[108,2072,2073],{},"The account no longer syncs.",[81,2075,2076,2077,2080,2081,2084],{},"The response is paginated, so keep requesting pages until ",[196,2078,2079],{},"meta.nextCursor"," is ",[196,2082,2083],{},"null",". To check a single account, call:",[324,2086,2088],{"className":1906,"code":2087,"language":1908,"meta":330,"style":330},"GET \u002Fv2\u002Faccounts\u002F{accountId}\u002Fstatus\n",[196,2089,2090],{"__ignoreMap":330},[334,2091,2092,2094],{"class":336,"line":337},[334,2093,1915],{"class":340},[334,2095,2096],{"class":344}," \u002Fv2\u002Faccounts\u002F{accountId}\u002Fstatus\n",[81,2098,2099,2100,2080,2103,2106],{},"When ",[196,2101,2102],{},"overall.fix",[196,2104,2105],{},"reconnect",", the user needs to sign in again.",[81,2108,2109,2112,2113,2115,2116,2119],{},[148,2110,2111],{},"With PyasConnect",", send the account's ",[196,2114,543],{}," from your page to your endpoint, and return it from the function you pass to ",[196,2117,2118],{},"sessionHandler",". The dialog then opens directly to that account:",[321,2121,2122,2188],{},[324,2123,2126],{"className":326,"code":2124,"filename":2125,"language":329,"meta":330,"style":330},"app.use(express.json())\n\napp.post('\u002Fapi\u002Fpyas-reconnect', pyas.connect.sessionHandler((req) => ({ accountId: req.body.accountId })))\n","Your server",[196,2127,2128,2144,2148],{"__ignoreMap":330},[334,2129,2130,2132,2135,2137,2140,2142],{"class":336,"line":337},[334,2131,412],{"class":368},[334,2133,2134],{"class":378},".use",[334,2136,418],{"class":344},[334,2138,2139],{"class":368},"express",[334,2141,620],{"class":378},[334,2143,438],{"class":344},[334,2145,2146],{"class":336,"line":355},[334,2147,359],{"emptyLinePlaceholder":358},[334,2149,2150,2152,2154,2156,2159,2161,2163,2165,2167,2169,2171,2173,2176,2178,2180,2182,2185],{"class":336,"line":362},[334,2151,412],{"class":368},[334,2153,415],{"class":378},[334,2155,418],{"class":344},[334,2157,2158],{"class":351},"'\u002Fapi\u002Fpyas-reconnect'",[334,2160,425],{"class":424},[334,2162,369],{"class":368},[334,2164,151],{"class":378},[334,2166,432],{"class":368},[334,2168,435],{"class":378},[334,2170,512],{"class":344},[334,2172,515],{"class":340},[334,2174,2175],{"class":344}," ({ accountId",[334,2177,385],{"class":340},[334,2179,518],{"class":368},[334,2181,151],{"class":344},[334,2183,2184],{"class":368},"body",[334,2186,2187],{"class":344},".accountId })))\n",[324,2189,2192],{"className":326,"code":2190,"filename":2191,"language":329,"meta":330,"style":330},"const getSessionToken = () =>\n  fetch('\u002Fapi\u002Fpyas-reconnect', {\n    method: 'POST',\n    headers: { 'content-type': 'application\u002Fjson' },\n    body: JSON.stringify({ accountId }),\n  }).then((r) => r.json()).then((s) => s.token)\n","Your page",[196,2193,2194,2206,2218,2229,2251,2269],{"__ignoreMap":330},[334,2195,2196,2198,2200,2202,2204],{"class":336,"line":337},[334,2197,365],{"class":340},[334,2199,576],{"class":378},[334,2201,372],{"class":340},[334,2203,581],{"class":344},[334,2205,584],{"class":340},[334,2207,2208,2210,2212,2214,2216],{"class":336,"line":355},[334,2209,589],{"class":378},[334,2211,418],{"class":344},[334,2213,2158],{"class":351},[334,2215,425],{"class":424},[334,2217,1408],{"class":344},[334,2219,2220,2223,2225,2227],{"class":336,"line":362},[334,2221,2222],{"class":344},"    method",[334,2224,385],{"class":340},[334,2226,603],{"class":351},[334,2228,1063],{"class":424},[334,2230,2231,2234,2236,2238,2241,2243,2246,2249],{"class":336,"line":404},[334,2232,2233],{"class":344},"    headers",[334,2235,385],{"class":340},[334,2237,1299],{"class":344},[334,2239,2240],{"class":351},"'content-type'",[334,2242,385],{"class":340},[334,2244,2245],{"class":351}," 'application\u002Fjson'",[334,2247,2248],{"class":344}," }",[334,2250,1063],{"class":424},[334,2252,2253,2256,2258,2261,2264,2267],{"class":336,"line":409},[334,2254,2255],{"class":344},"    body",[334,2257,385],{"class":340},[334,2259,2260],{"class":368}," JSON",[334,2262,2263],{"class":378},".stringify",[334,2265,2266],{"class":344},"({ accountId })",[334,2268,1063],{"class":424},[334,2270,2271,2274,2276,2278,2280,2282,2284,2286,2288,2290,2292,2294],{"class":336,"line":643},[334,2272,2273],{"class":344},"  })",[334,2275,609],{"class":378},[334,2277,612],{"class":344},[334,2279,515],{"class":340},[334,2281,617],{"class":368},[334,2283,620],{"class":378},[334,2285,623],{"class":344},[334,2287,609],{"class":378},[334,2289,628],{"class":344},[334,2291,515],{"class":340},[334,2293,633],{"class":368},[334,2295,636],{"class":344},[81,2297,2298,2301,2302,1193,2304,2080,2307,2310],{},[148,2299,2300],{},"With your own screens",", send the same two requests again, first to get a sign-in URL and then to send the code. When the same person signs in, the API reconnects the account. The account keeps its ",[196,2303,543],{},[196,2305,2306],{},"existing",[196,2308,2309],{},"true"," in the response.",[81,2312,2313,2314,2317,2318,279,2321,1994,2324,2326],{},"Each account's status also includes ",[196,2315,2316],{},"userMessage",", a sentence you can show the user. The ",[196,2319,2320],{},"connection.broken",[196,2322,2323],{},"connection.reauthorized",[1171,2325,1998],{"href":1997}," events notify you when an account stops syncing and when it's reconnected.",[2328,2329,2330],"style",{},"html pre.shiki code .sag7P, html code.shiki .sag7P{--shiki-default:var(--shiki-token-keyword)}html pre.shiki code .sqSWz, html code.shiki .sqSWz{--shiki-default:var(--shiki-foreground)}html pre.shiki code .sk1Au, html code.shiki .sk1Au{--shiki-default:var(--shiki-token-string-expression)}html pre.shiki code .sXULy, html code.shiki .sXULy{--shiki-default:var(--shiki-token-constant)}html pre.shiki code .s1IGz, html code.shiki .s1IGz{--shiki-default:var(--shiki-token-function)}html pre.shiki code .sEEt9, html code.shiki .sEEt9{--shiki-default:var(--shiki-token-punctuation)}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .ssgCD, html code.shiki .ssgCD{--shiki-default:var(--shiki-token-comment)}html pre.shiki code .sTB4W, html code.shiki .sTB4W{--shiki-default:var(--shiki-token-string)}",{"title":330,"searchDepth":362,"depth":362,"links":2332},[2333,2334,2335,2339,2344,2345],{"id":51,"depth":355,"text":52},{"id":54,"depth":355,"text":55},{"id":57,"depth":355,"text":58,"children":2336},[2337,2338],{"id":315,"depth":362,"text":316},{"id":536,"depth":362,"text":537},{"id":60,"depth":355,"text":61,"children":2340},[2341,2342,2343],{"id":1231,"depth":362,"text":1232},{"id":1553,"depth":362,"text":1554},{"id":1577,"depth":362,"text":1578},{"id":63,"depth":355,"text":64},{"id":66,"depth":355,"text":67},"md",false,{},"\u002Fconnect","---\ntitle: Connecting accounts\ndescription: Let your users connect their Google Calendar, Microsoft Outlook and Zoom accounts, with PyasConnect or with your own screens.\nv1: \u002Ffundamentals\u002Fgetting-set-up\u002Fsetup-authentication\u002Fconnect-an-account\n---\n\n# Connecting accounts\n\n::lead\nBefore you can read or write a user's calendar, the user connects their Google, Microsoft Outlook or Zoom account to your app. You can set this up in two ways. You can add PyasConnect, a ready-made sign-in dialog, to your page, or you can build your own screens on the API.\n::\n\n| | PyasConnect | Your own screens |\n|---|---|---|\n| What you build | One endpoint on your server, and the component on your page | The provider buttons, the redirects, and two requests from your server |\n| Requests from your server | One, from your endpoint | Two, one to get a sign-in URL and one to send the code |\n| Works with | Pyas Auth and Native Auth | Pyas Auth and Native Auth |\n\nWith either option, your API key stays on your server, and only your server uses it. To connect real accounts, you need a live key. A test key works only in the sandbox, which already has its own accounts.\n\n## Pyas Auth or Native Auth\n\nFor each provider, you choose how people sign in. You set this in the dashboard, under your app's **Provider setup**.\n\n- **Pyas Auth** is the default. People sign in through Pyas's own OAuth app for Google, Microsoft or Zoom, so you don't need to set anything up. The provider's consent screen shows the Pyas name.\n- **Native Auth** uses your own OAuth app for Google, Microsoft or Zoom. The provider's consent screen shows your app's name. To use it, enter your OAuth app's client ID, its client secret and your callback URL under **Provider setup**.\n\n## Additional scopes\n\nBy default, a connection asks for the access most apps need: reading and writing events, reading calendars, free\u002Fbusy and settings. Some features need more access, so the user is asked for it only when your app requests the feature. There are two ways to request them:\n\n- **In the dashboard,** under your app's **Provider setup**, in **Additional scopes**. Choose them once for each provider, and every connection from your app asks for them, in PyasConnect and on your own screens.\n- **In a request,** with `features` when you create a connect session or a sign-in URL. A request that includes `features` uses those instead of the dashboard's choice.\n\n| Feature | What it adds |\n|---|---|\n| `manageCalendars` | Create, rename, recolor and delete calendars, and add calendars to the person's list or remove them. |\n| `sharing` | Share a calendar with other people and change their access. |\n| `meetingSettings` | Change Google Meet and Microsoft Teams settings, such as who can join without waiting. |\n| `attachmentUploads` | Upload files to events. |\n| `rooms` | List meeting rooms and book them for events. |\n\nOn Google, `manageCalendars`, `sharing`, `meetingSettings`, `rooms` and `attachmentUploads` need Native Auth for now. Google must approve this access for Pyas's own Google app first. If your app uses Pyas Auth for Google and requests one of them in `features`, the request fails with `CONNECTION_PROVIDER_NOT_CONFIGURED`. In the dashboard, they can't be chosen until then. With Native Auth, add each feature's scopes to your own Google app; the dashboard lists them, and any Google API to turn on.\n\nOn Microsoft, every feature works with either option. For `rooms`, the user's Microsoft 365 administrator must approve the access, so people whose organization hasn't approved it can't connect while you request it.\n\n## Option 1: PyasConnect\n\nPyasConnect is a sign-in dialog that you add to your page. It shows the provider buttons, handles the sign-in, blocked pop-ups and errors, and saves the connected account. Before you start, add your site to your app's **allowed origins** in the dashboard. PyasConnect works only on those origins.\n\nTo try it first, open **Lab** in the dashboard. Pick your app, change how PyasConnect looks and behaves, and connect a real account in the preview, with no server code. The Lab then gives you the code for Vue, React, Angular or plain HTML, already filled in for your app.\n\n### 1. Add an endpoint to your server\n\nPyasConnect calls this endpoint each time a user opens the dialog. With the SDK, the endpoint is one line of code:\n\n::code-group\n```js [Express]\nimport { Pyas } from '@pyas\u002Fsdk'\n\nconst pyas = new Pyas({ apiKey: process.env.PYAS_API_KEY })\n\napp.post('\u002Fapi\u002Fpyas-session', pyas.connect.sessionHandler())\n```\n\n```js [Next.js]\n\u002F\u002F app\u002Fapi\u002Fpyas-session\u002Froute.js\nexport const POST = pyas.connect.sessionHandler()\n```\n::\n\n**Optional: Save your own user id on the account.** If your app has its own id for each user, pass a function that returns the id of the user who is connecting. Pyas saves the id on the account as `subjectId`, so you can find that user's accounts later.\n\n```js\napp.post('\u002Fapi\u002Fpyas-session', pyas.connect.sessionHandler((req) => req.user.id))\n```\n\nReplace `req.user.id` with wherever your app stores the id of the signed-in user.\n\n### 2. Add the component to your page\n\nGive the component a function that calls your endpoint and returns the session token. When an account is connected, save its `accountId`:\n\n::code-group\n```jsx [React]\nimport { PyasConnect } from '@pyas\u002Fconnect-react'\n\nconst getSessionToken = () =>\n  fetch('\u002Fapi\u002Fpyas-session', { method: 'POST' }).then((r) => r.json()).then((s) => s.token)\n\n\u003CPyasConnect getSessionToken={getSessionToken} onConnected={(account) => save(account.accountId)} \u002F>\n```\n\n```vue [Vue]\n\u003Cscript setup>\nimport { PyasConnect } from '@pyas\u002Fconnect-vue'\n\nconst getSessionToken = () =>\n  fetch('\u002Fapi\u002Fpyas-session', { method: 'POST' }).then((r) => r.json()).then((s) => s.token)\n\u003C\u002Fscript>\n\n\u003Ctemplate>\n  \u003CPyasConnect :get-session-token=\"getSessionToken\" @connected=\"(account) => save(account.accountId)\" \u002F>\n\u003C\u002Ftemplate>\n```\n\n```html [HTML]\n\u003Cscript type=\"module\" src=\"https:\u002F\u002Fcdn.jsdelivr.net\u002Fnpm\u002F@pyas\u002Fconnect@2\u002Fdist\u002Fpyas-connect\u002Fpyas-connect.esm.js\">\u003C\u002Fscript>\n\n\u003Cpyas-connect>\u003C\u002Fpyas-connect>\n\u003Cscript>\n  const connect = document.querySelector('pyas-connect')\n  connect.getSessionToken = () =>\n    fetch('\u002Fapi\u002Fpyas-session', { method: 'POST' }).then((r) => r.json()).then((s) => s.token)\n  connect.addEventListener('connected', (e) => save(e.detail.accountId))\n\u003C\u002Fscript>\n```\n::\n\nPyasConnect shows a \"Connect an account\" button, with your app's name and icon from the dashboard. When a user connects an account, your handler receives the account:\n\n```json\n{\n  \"accountId\": \"6b0e3f1a-8c2d-4e5f-9a7b-1c3d5e7f9a2b\",\n  \"provider\": \"google\",\n  \"email\": \"avery@yourcustomer.com\",\n  \"name\": \"Avery Brooks\",\n  \"subjectId\": \"user_42\"\n}\n```\n\nWith **Native Auth**, the provider redirects people to your callback URL instead of to Pyas. Add this code to your callback page so that it passes the result to the dialog:\n\n```js\nimport { postCallbackResult } from '@pyas\u002Fconnect'\npostCallbackResult()\n```\n\nThe dialog is available in English, Norwegian, Swedish, Danish, German, French, Spanish, Dutch and Portuguese (Brazil). It uses your page's language (`\u003Chtml lang>`), then the browser's. To choose the language yourself, set `locale`, for example `locale=\"de\"`. The pages Pyas shows in redirect mode work the same way: they use the browser's language, or add `&locale=de` to the session's `url`.\n\nFor themes, custom text, a custom button and every other option, see the package documentation on npm: [`@pyas\u002Fconnect-react`](https:\u002F\u002Fwww.npmjs.com\u002Fpackage\u002F@pyas\u002Fconnect-react), [`@pyas\u002Fconnect-vue`](https:\u002F\u002Fwww.npmjs.com\u002Fpackage\u002F@pyas\u002Fconnect-vue), [`@pyas\u002Fconnect-angular`](https:\u002F\u002Fwww.npmjs.com\u002Fpackage\u002F@pyas\u002Fconnect-angular), and [`@pyas\u002Fconnect`](https:\u002F\u002Fwww.npmjs.com\u002Fpackage\u002F@pyas\u002Fconnect) for any other page.\n\n## Option 2: Your own screens\n\nWith this option, you show the provider buttons yourself. When a user selects one, your server requests a sign-in URL from the API and redirects the user to it. After the user signs in, they return to your app with a code, and your server sends that code to the API to save the account.\n\nFirst, set the page that users return to after they sign in. You do this under **Provider setup** in the dashboard:\n\n- **Pyas Auth**: Set the provider's **Redirect URL** to the page in your app that receives the code. Pyas redirects people to that page after they sign in.\n- **Native Auth**: Your callback URL is already set in Provider setup, and the provider redirects people to it.\n\n### 1. Get a sign-in URL\n\nWhen the user selects a provider button, request a sign-in URL from your server, then redirect the user to the `url` in the response:\n\n::code-group\n```js [Node.js]\nimport { Pyas } from '@pyas\u002Fsdk'\n\nconst pyas = new Pyas({ apiKey: process.env.PYAS_API_KEY })\n\nconst { url } = await pyas.connect.oauth({ provider: 'google', state: 'user_42' })\nres.redirect(url)\n```\n\n```bash [cURL]\ncurl \"https:\u002F\u002Fapi.pyas.io\u002Fv2\u002Fconnect\u002Foauth?provider=google&state=user_42\" \\\n  -H \"x-api-key: $PYAS_API_KEY\"\n```\n::\n\n```json\n{\n  \"success\": true,\n  \"data\": {\n    \"provider\": \"google\",\n    \"url\": \"https:\u002F\u002Faccounts.google.com\u002Fo\u002Foauth2\u002Fv2\u002Fauth?client_id=...&redirect_uri=...&response_type=code&scope=...&state=...\"\n  },\n  \"provider\": null,\n  \"traceId\": \"trc_5f2a9c1e7b3d4a6f8e0c2b1d\",\n  \"meta\": {}\n}\n```\n\n| Parameter | Description |\n|---|---|\n| `provider` | `google`, `microsoft` or `zoom`. Required. |\n| `state` | Any value you choose, such as your id for the user. It's returned to you with the code. |\n| `loginHint` | An email address to prefill on the provider's sign-in page. |\n| `features` | Additional access to request, as a comma-separated list of `manageCalendars`, `sharing`, `meetingSettings`, `attachmentUploads` and `rooms`. See [Additional scopes](#additional-scopes). |\n\n### 2. The user returns with a code\n\nAfter signing in, the user is redirected to your Redirect URL (Pyas Auth) or your callback URL (Native Auth). The URL includes `code` and your `state` as query parameters:\n\n```\nhttps:\u002F\u002Fyourapp.com\u002Fconnected\u002Fgoogle?code=4\u002F0AeanS0Z...&state=user_42\n```\n\nIf the user cancels or the provider denies access, there's no valid code, and the request in step 3 returns an error. Show your own message and let the user try again.\n\n### 3. Exchange the code for an account\n\nFrom your server, send the code to the API. The API saves the account and returns it:\n\n::code-group\n```js [Node.js]\nconst account = await pyas.connect.accounts.create({\n  provider: 'google',\n  code: req.query.code,\n  subjectId: req.query.state,\n})\nawait saveAccount(user, account.accountId)\n```\n\n```bash [cURL]\ncurl -X POST https:\u002F\u002Fapi.pyas.io\u002Fv2\u002Fconnect\u002Faccounts \\\n  -H \"x-api-key: $PYAS_API_KEY\" \\\n  -H \"content-type: application\u002Fjson\" \\\n  -d '{ \"provider\": \"google\", \"code\": \"4\u002F0AeanS0Z...\", \"subjectId\": \"user_42\" }'\n```\n::\n\n```json\n{\n  \"success\": true,\n  \"data\": {\n    \"accountId\": \"6b0e3f1a-8c2d-4e5f-9a7b-1c3d5e7f9a2b\",\n    \"provider\": \"google\",\n    \"email\": \"avery@yourcustomer.com\",\n    \"name\": \"Avery Brooks\",\n    \"subjectId\": \"user_42\",\n    \"existing\": false,\n    \"grantedScopes\": [\"https:\u002F\u002Fwww.googleapis.com\u002Fauth\u002Fcalendar\"]\n  },\n  \"provider\": null,\n  \"traceId\": \"trc_5f2a9c1e7b3d4a6f8e0c2b1d\",\n  \"meta\": {}\n}\n```\n\n`subjectId` is optional. It's your own id for the user, and the API saves it on the account. Each code can be used only once. If the request fails, the error explains why, for example with `CONNECTION_CONSENT_FAILED` or `BILLING_CONNECTION_LIMIT`. See [Errors](\u002Ferrors).\n\nIf you're coming from v1, steps 1 and 3 replace these two v1 endpoints:\n\n```http\nGET \u002F{provider}\u002Foauth\nPOST \u002F{provider}\u002Fconnect\n```\n\nIn v2, the provider is a parameter instead of part of the path. The v1 endpoints still work.\n\n## Save the account id\n\nWith either option, store the `accountId` with your user. Every request that reads or writes their calendar must include it. To find a user's accounts later, list them by the id you saved as `subjectId`:\n\n```js\nfor await (const account of pyas.accounts.list({ subjectId: user.id })) { \u002F* ... *\u002F }\n```\n\nPyas also sends a `connection.created` [webhook](\u002Fwebhooks) event for every new account.\n\n## When a connection breaks\n\nA connection can stop working when a user revokes access, changes their password or leaves their company. To find the accounts that need attention, call:\n\n```http\nGET \u002Fv2\u002Faccounts\u002Fstatus?overall=attention,broken\n```\n\nEach account in the response has an `overall` status:\n\n| `overall.status` | Meaning |\n|---|---|\n| `healthy` | The connection works. |\n| `attention` | The connection works, but some features are limited, for example because of a missing permission. `reason` explains the limitation. |\n| `broken` | The account no longer syncs. |\n\nThe response is paginated, so keep requesting pages until `meta.nextCursor` is `null`. To check a single account, call:\n\n```http\nGET \u002Fv2\u002Faccounts\u002F{accountId}\u002Fstatus\n```\n\nWhen `overall.fix` is `reconnect`, the user needs to sign in again.\n\n**With PyasConnect**, send the account's `accountId` from your page to your endpoint, and return it from the function you pass to `sessionHandler`. The dialog then opens directly to that account:\n\n::code-group\n```js [Your server]\napp.use(express.json())\n\napp.post('\u002Fapi\u002Fpyas-reconnect', pyas.connect.sessionHandler((req) => ({ accountId: req.body.accountId })))\n```\n\n```js [Your page]\nconst getSessionToken = () =>\n  fetch('\u002Fapi\u002Fpyas-reconnect', {\n    method: 'POST',\n    headers: { 'content-type': 'application\u002Fjson' },\n    body: JSON.stringify({ accountId }),\n  }).then((r) => r.json()).then((s) => s.token)\n```\n::\n\n**With your own screens**, send the same two requests again, first to get a sign-in URL and then to send the code. When the same person signs in, the API reconnects the account. The account keeps its `accountId`, and `existing` is `true` in the response.\n\nEach account's status also includes `userMessage`, a sentence you can show the user. The `connection.broken` and `connection.reauthorized` [webhook](\u002Fwebhooks) events notify you when an account stops syncing and when it's reconnected.\n",null,{"title":47,"description":48},"\u002Ffundamentals\u002Fgetting-set-up\u002Fsetup-authentication\u002Fconnect-an-account","6yz3C8tGa9qtlPdh2okzabqKuwCAJRu3MWsTFjuE1hQ",1794697271283]