[{"data":1,"prerenderedAt":857},["ShallowReactive",2],{"navigation":3,"page:\u002Flovable":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","Build with Lovable","Add Google Calendar and Microsoft Outlook to a Lovable app. Connect the Pyas MCP server to Lovable, and its agent adds the server code, the secret and the connect button.",[50,53,56,59,62,65],{"id":51,"title":52},"add-pyas-to-lovable","Add Pyas to Lovable",{"id":54,"title":55},"ask-for-what-you-want","Ask for what you want",{"id":57,"title":58},"store-the-key","Store the key",{"id":60,"title":61},"what-lovable-adds","What Lovable adds",{"id":63,"title":64},"receive-webhooks","Receive webhooks",{"id":66,"title":67},"go-live","Go live",{"id":69,"title":47,"body":70,"description":48,"extension":848,"hero":849,"meta":850,"navigation":279,"path":851,"rawbody":852,"sections":49,"seo":853,"stem":854,"v1":855,"__hash__":856},"docs\u002Flovable.md",{"type":71,"value":72,"toc":840},"minimark",[73,77,84,87,146,152,157,159,162,168,187,189,196,217,224,226,229,239,246,761,764,774,776,783,790,796,809,811,836],[74,75,47],"h1",{"id":76},"build-with-lovable",[78,79,80],"lead",{},[81,82,83],"p",{},"Lovable's agent can add Google Calendar and Microsoft Outlook to your app with Pyas. You connect the Pyas MCP server to Lovable once. The agent then gets the code for each task from Pyas, and you store a key as a secret. The key stays on the server, and the code works with the sandbox's sample people before anyone connects a real calendar.",[85,86,52],"h2",{"id":51},[88,89,90,107,129],"ol",{},[91,92,93,94,98,99,102,103,106],"li",{},"In Lovable, open ",[95,96,97],"strong",{},"Connectors",", select ",[95,100,101],{},"Add custom"," at the top right, then select ",[95,104,105],{},"MCP server",".",[91,108,109,110,113,114,118,119,113,122,125,126,106],{},"For ",[95,111,112],{},"Server name",", enter ",[115,116,117],"code",{},"Pyas",". For ",[95,120,121],{},"Server URL",[115,123,124],{},"https:\u002F\u002Fmcp.pyas.io\u002Fsandbox",". Leave the authentication on ",[95,127,128],{},"OAuth",[91,130,131,132,135,136,139,140,145],{},"Select ",[95,133,134],{},"Add & authorize",". Sign in to Pyas, choose your app, and select ",[95,137,138],{},"Allow",". See ",[141,142,144],"a",{"href":143},"\u002Fai-tools#sign-in","Sign in"," for what each permission allows.",[81,147,148,149,151],{},"The connection is yours. It gives Lovable's agent the Pyas tools while you build, and it is never part of your published app. Custom MCP servers are available on every Lovable plan. If ",[95,150,105],{}," can't be selected, an admin of your workspace has turned custom servers off.",[81,153,154,155,106],{},"The agent uses the Pyas tools that only read without asking. Before a tool that changes something, such as creating a test key, it shows a card for you to approve. To stop or remove the connection, open ",[95,156,97],{},[85,158,55],{"id":54},[81,160,161],{},"Ask in plain words, for example:",[163,164,165],"blockquote",{},[81,166,167],{},"Add a button that lets people connect their Google or Outlook calendar, and show their events for the next week.",[81,169,170,171,174,175,178,179,182,183,186],{},"The agent checks your setup with ",[115,172,173],{},"setup_status"," and gets the code for each task with ",[115,176,177],{},"get_recipe",". It adds your app's preview address to the allowed origins with ",[115,180,181],{},"update_app",", and creates a test key with ",[115,184,185],{},"get_test_key",". You confirm the address in Pyas, from the link it gives you. Every response from Pyas ends with the next step, so the agent knows what to do next.",[85,188,58],{"id":57},[81,190,191,192,195],{},"Store the test key as a secret named ",[115,193,194],{},"PYAS_API_KEY",":",[88,197,198,210],{},[91,199,93,200,203,204,203,207,106],{},[95,201,202],{},"More",", then ",[95,205,206],{},"Settings",[95,208,209],{},"Secrets",[91,211,212,213,216],{},"Use the ",[95,214,215],{},"Add secret"," form. Don't paste the key into the chat.",[81,218,219,220,106],{},"Your app's server code reads the secret, and it never reaches the browser. A test key works only in your app's ",[141,221,223],{"href":222},"\u002Fsandbox","sandbox",[85,225,61],{"id":60},[81,227,228],{},"Lovable projects created since May 13, 2026 run on TanStack Start, and the code runs as server functions inside your app:",[230,231,237],"pre",{"className":232,"code":234,"language":235,"meta":236},[233],"language-text","src\u002Flib\u002Fpyas.server.ts                the Pyas client, on the server only\nsrc\u002Flib\u002Fpyas-connect.functions.ts     creates a connect session for the button\nsrc\u002Flib\u002Fpyas-events.functions.ts      the person's events for the next 7 days\nsrc\u002Flib\u002Fpyas-book.functions.ts        books the first free 30 minutes tomorrow\nsrc\u002Fcomponents\u002FConnectCalendar.tsx    the \"Connect an account\" button\n","text","",[115,238,234],{"__ignoreMap":236},[81,240,241,242,245],{},"Your pages call the functions directly, for example ",[115,243,244],{},"await listEvents()",". The function that lists events looks like this:",[230,247,252],{"className":248,"code":249,"filename":250,"language":251,"meta":236,"style":236},"language-ts shiki shiki-themes css-variables","import { createServerFn } from '@tanstack\u002Freact-start'\n\nconst DAY = 24 * 60 * 60 * 1000\n\nexport const listEvents = createServerFn({ method: 'GET' }).handler(async ({ context }) => {\n    \u002F\u002F Loaded here, on the server: this file is also part of the browser's code.\n    const { errorResult, pyas, signInRequired, subjectFor } = await import('.\u002Fpyas.server')\n    const subjectId = subjectFor(context)\n    if (!subjectId) return signInRequired()\n    try {\n        const now = Date.now()\n        const page = await pyas().events.list({\n            subjectId,\n            from: new Date(now).toISOString(),\n            to: new Date(now + 7 * DAY).toISOString(),\n            expand: true,\n            sort: 'start',\n            limit: 50,\n        })\n        \u002F\u002F Titles are written by whoever sent the invitation: show them as text, never as HTML.\n        return { events: page.data.map((event) => ({ id: event.id, title: event.title, startsAt: event.startsAt, endsAt: event.endsAt, isAllDay: event.isAllDay, provider: event.provider })) }\n    } catch (error) {\n        return errorResult(error)\n    }\n})\n","src\u002Flib\u002Fpyas-events.functions.ts","ts",[115,253,254,274,281,312,317,364,371,420,435,458,466,485,511,520,544,578,591,604,617,623,629,726,738,749,755],{"__ignoreMap":236},[255,256,259,263,267,270],"span",{"class":257,"line":258},"line",1,[255,260,262],{"class":261},"sag7P","import",[255,264,266],{"class":265},"sqSWz"," { createServerFn } ",[255,268,269],{"class":261},"from",[255,271,273],{"class":272},"sk1Au"," '@tanstack\u002Freact-start'\n",[255,275,277],{"class":257,"line":276},2,[255,278,280],{"emptyLinePlaceholder":279},true,"\n",[255,282,284,287,291,294,297,300,303,305,307,309],{"class":257,"line":283},3,[255,285,286],{"class":261},"const",[255,288,290],{"class":289},"sXULy"," DAY",[255,292,293],{"class":261}," =",[255,295,296],{"class":289}," 24",[255,298,299],{"class":261}," *",[255,301,302],{"class":289}," 60",[255,304,299],{"class":261},[255,306,302],{"class":289},[255,308,299],{"class":261},[255,310,311],{"class":289}," 1000\n",[255,313,315],{"class":257,"line":314},4,[255,316,280],{"emptyLinePlaceholder":279},[255,318,320,323,326,329,331,335,338,340,343,346,349,352,355,358,361],{"class":257,"line":319},5,[255,321,322],{"class":261},"export",[255,324,325],{"class":261}," const",[255,327,328],{"class":289}," listEvents",[255,330,293],{"class":261},[255,332,334],{"class":333},"s1IGz"," createServerFn",[255,336,337],{"class":265},"({ method",[255,339,195],{"class":261},[255,341,342],{"class":272}," 'GET'",[255,344,345],{"class":265}," })",[255,347,348],{"class":333},".handler",[255,350,351],{"class":265},"(",[255,353,354],{"class":261},"async",[255,356,357],{"class":265}," ({ context }) ",[255,359,360],{"class":261},"=>",[255,362,363],{"class":265}," {\n",[255,365,367],{"class":257,"line":366},6,[255,368,370],{"class":369},"ssgCD","    \u002F\u002F Loaded here, on the server: this file is also part of the browser's code.\n",[255,372,374,377,380,383,387,390,392,395,397,400,403,406,409,412,414,417],{"class":257,"line":373},7,[255,375,376],{"class":261},"    const",[255,378,379],{"class":265}," { ",[255,381,382],{"class":289},"errorResult",[255,384,386],{"class":385},"sEEt9",",",[255,388,389],{"class":289}," pyas",[255,391,386],{"class":385},[255,393,394],{"class":289}," signInRequired",[255,396,386],{"class":385},[255,398,399],{"class":289}," subjectFor",[255,401,402],{"class":265}," } ",[255,404,405],{"class":261},"=",[255,407,408],{"class":261}," await",[255,410,411],{"class":261}," import",[255,413,351],{"class":265},[255,415,416],{"class":272},"'.\u002Fpyas.server'",[255,418,419],{"class":265},")\n",[255,421,423,425,428,430,432],{"class":257,"line":422},8,[255,424,376],{"class":261},[255,426,427],{"class":289}," subjectId",[255,429,293],{"class":261},[255,431,399],{"class":333},[255,433,434],{"class":265},"(context)\n",[255,436,438,441,444,447,450,453,455],{"class":257,"line":437},9,[255,439,440],{"class":261},"    if",[255,442,443],{"class":265}," (",[255,445,446],{"class":261},"!",[255,448,449],{"class":265},"subjectId) ",[255,451,452],{"class":261},"return",[255,454,394],{"class":333},[255,456,457],{"class":265},"()\n",[255,459,461,464],{"class":257,"line":460},10,[255,462,463],{"class":261},"    try",[255,465,363],{"class":265},[255,467,469,472,475,477,480,483],{"class":257,"line":468},11,[255,470,471],{"class":261},"        const",[255,473,474],{"class":289}," now",[255,476,293],{"class":261},[255,478,479],{"class":289}," Date",[255,481,482],{"class":333},".now",[255,484,457],{"class":265},[255,486,488,490,493,495,497,499,502,505,508],{"class":257,"line":487},12,[255,489,471],{"class":261},[255,491,492],{"class":289}," page",[255,494,293],{"class":261},[255,496,408],{"class":261},[255,498,389],{"class":333},[255,500,501],{"class":265},"().",[255,503,504],{"class":289},"events",[255,506,507],{"class":333},".list",[255,509,510],{"class":265},"({\n",[255,512,514,517],{"class":257,"line":513},13,[255,515,516],{"class":265},"            subjectId",[255,518,519],{"class":385},",\n",[255,521,523,526,528,531,533,536,539,542],{"class":257,"line":522},14,[255,524,525],{"class":265},"            from",[255,527,195],{"class":261},[255,529,530],{"class":261}," new",[255,532,479],{"class":333},[255,534,535],{"class":265},"(now)",[255,537,538],{"class":333},".toISOString",[255,540,541],{"class":265},"()",[255,543,519],{"class":385},[255,545,547,550,552,554,556,559,562,565,567,569,572,574,576],{"class":257,"line":546},15,[255,548,549],{"class":265},"            to",[255,551,195],{"class":261},[255,553,530],{"class":261},[255,555,479],{"class":333},[255,557,558],{"class":265},"(now ",[255,560,561],{"class":261},"+",[255,563,564],{"class":289}," 7",[255,566,299],{"class":261},[255,568,290],{"class":289},[255,570,571],{"class":265},")",[255,573,538],{"class":333},[255,575,541],{"class":265},[255,577,519],{"class":385},[255,579,581,584,586,589],{"class":257,"line":580},16,[255,582,583],{"class":265},"            expand",[255,585,195],{"class":261},[255,587,588],{"class":289}," true",[255,590,519],{"class":385},[255,592,594,597,599,602],{"class":257,"line":593},17,[255,595,596],{"class":265},"            sort",[255,598,195],{"class":261},[255,600,601],{"class":272}," 'start'",[255,603,519],{"class":385},[255,605,607,610,612,615],{"class":257,"line":606},18,[255,608,609],{"class":265},"            limit",[255,611,195],{"class":261},[255,613,614],{"class":289}," 50",[255,616,519],{"class":385},[255,618,620],{"class":257,"line":619},19,[255,621,622],{"class":265},"        })\n",[255,624,626],{"class":257,"line":625},20,[255,627,628],{"class":369},"        \u002F\u002F Titles are written by whoever sent the invitation: show them as text, never as HTML.\n",[255,630,632,635,638,640,642,644,647,650,653,655,658,660,663,666,668,671,673,675,678,680,683,685,687,690,692,695,697,699,702,704,707,709,711,714,716,719,721,723],{"class":257,"line":631},21,[255,633,634],{"class":261},"        return",[255,636,637],{"class":265}," { events",[255,639,195],{"class":261},[255,641,492],{"class":289},[255,643,106],{"class":333},[255,645,646],{"class":289},"data",[255,648,649],{"class":333},".map",[255,651,652],{"class":265},"((event) ",[255,654,360],{"class":261},[255,656,657],{"class":265}," ({ id",[255,659,195],{"class":261},[255,661,662],{"class":289}," event",[255,664,665],{"class":265},".id",[255,667,386],{"class":385},[255,669,670],{"class":265}," title",[255,672,195],{"class":261},[255,674,662],{"class":289},[255,676,677],{"class":265},".title",[255,679,386],{"class":385},[255,681,682],{"class":265}," startsAt",[255,684,195],{"class":261},[255,686,662],{"class":289},[255,688,689],{"class":265},".startsAt",[255,691,386],{"class":385},[255,693,694],{"class":265}," endsAt",[255,696,195],{"class":261},[255,698,662],{"class":289},[255,700,701],{"class":265},".endsAt",[255,703,386],{"class":385},[255,705,706],{"class":265}," isAllDay",[255,708,195],{"class":261},[255,710,662],{"class":289},[255,712,713],{"class":265},".isAllDay",[255,715,386],{"class":385},[255,717,718],{"class":265}," provider",[255,720,195],{"class":261},[255,722,662],{"class":289},[255,724,725],{"class":265},".provider })) }\n",[255,727,729,732,735],{"class":257,"line":728},22,[255,730,731],{"class":265},"    } ",[255,733,734],{"class":261},"catch",[255,736,737],{"class":265}," (error) {\n",[255,739,741,743,746],{"class":257,"line":740},23,[255,742,634],{"class":261},[255,744,745],{"class":333}," errorResult",[255,747,748],{"class":265},"(error)\n",[255,750,752],{"class":257,"line":751},24,[255,753,754],{"class":265},"    }\n",[255,756,758],{"class":257,"line":757},25,[255,759,760],{"class":265},"})\n",[81,762,763],{},"With a test key, the functions use the sandbox's sample person, so the events and booking work right away. The connect button shows a message from Pyas that connecting a real calendar requires a live key. This is expected.",[81,765,766,767,770,771,106],{},"In an older Lovable project, built with React and Vite, the agent adds Supabase Edge Functions in ",[115,768,769],{},"supabase\u002Ffunctions\u002F"," instead, and your pages call them with ",[115,772,773],{},"supabase.functions.invoke('pyas-events')",[85,775,64],{"id":63},[81,777,778,779,782],{},"Ask the agent to receive Pyas webhooks. It adds a public route, ",[115,780,781],{},"src\u002Froutes\u002Fapi\u002Fpublic\u002Fpyas-webhooks.ts",", which verifies the signature of each delivery against the raw body before it reads the event.",[81,784,785,786,789],{},"The agent adds the endpoint to your sandbox with ",[115,787,788],{},"create_webhook",", at your published address:",[230,791,794],{"className":792,"code":793,"language":235,"meta":236},[233],"https:\u002F\u002F\u003Cyour-app>.lovable.app\u002Fapi\u002Fpublic\u002Fpyas-webhooks\n",[115,795,793],{"__ignoreMap":236},[81,797,798,799,801,802,139,805,808],{},"Store the signing secret that ",[115,800,788],{}," returns as a secret named ",[115,803,804],{},"PYAS_WEBHOOK_SECRET",[141,806,28],{"href":807},"\u002Fwebhooks"," for the events Pyas sends.",[85,810,67],{"id":66},[88,812,813,820,823,829],{},[91,814,815,816,819],{},"Your app needs sign-in before people connect a calendar. Ask the agent to add sign-in, and to add it to the Pyas server functions. The functions then use the signed-in person. Until then, they respond with the code ",[115,817,818],{},"SIGN_IN_REQUIRED"," to every request made with a live key, so people's calendars never mix.",[91,821,822],{},"In the Pyas dashboard, choose a plan and create a live key. Plans, payment and live keys are always done by you, in the dashboard.",[91,824,825,826,828],{},"Replace the ",[115,827,194],{}," secret with the live key.",[91,830,831,832,835],{},"Add your published address, such as ",[115,833,834],{},"https:\u002F\u002Fyour-app.lovable.app",", to the app's allowed origins, and add a live webhook endpoint in the dashboard.",[837,838,839],"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 .ssgCD, html code.shiki .ssgCD{--shiki-default:var(--shiki-token-comment)}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);}",{"title":236,"searchDepth":283,"depth":283,"links":841},[842,843,844,845,846,847],{"id":51,"depth":276,"text":52},{"id":54,"depth":276,"text":55},{"id":57,"depth":276,"text":58},{"id":60,"depth":276,"text":61},{"id":63,"depth":276,"text":64},{"id":66,"depth":276,"text":67},"md",false,{},"\u002Flovable","---\ntitle: Build with Lovable\ndescription: Add Google Calendar and Microsoft Outlook to a Lovable app. Connect the Pyas MCP server to Lovable, and its agent adds the server code, the secret and the connect button.\nsections:\n  - { id: add-pyas-to-lovable, title: Add Pyas to Lovable }\n  - { id: ask-for-what-you-want, title: Ask for what you want }\n  - { id: store-the-key, title: Store the key }\n  - { id: what-lovable-adds, title: What Lovable adds }\n  - { id: receive-webhooks, title: Receive webhooks }\n  - { id: go-live, title: Go live }\n---\n\n# Build with Lovable\n\n::lead\nLovable's agent can add Google Calendar and Microsoft Outlook to your app with Pyas. You connect the Pyas MCP server to Lovable once. The agent then gets the code for each task from Pyas, and you store a key as a secret. The key stays on the server, and the code works with the sandbox's sample people before anyone connects a real calendar.\n::\n\n## Add Pyas to Lovable\n\n1. In Lovable, open **Connectors**, select **Add custom** at the top right, then select **MCP server**.\n2. For **Server name**, enter `Pyas`. For **Server URL**, enter `https:\u002F\u002Fmcp.pyas.io\u002Fsandbox`. Leave the authentication on **OAuth**.\n3. Select **Add & authorize**. Sign in to Pyas, choose your app, and select **Allow**. See [Sign in](\u002Fai-tools#sign-in) for what each permission allows.\n\nThe connection is yours. It gives Lovable's agent the Pyas tools while you build, and it is never part of your published app. Custom MCP servers are available on every Lovable plan. If **MCP server** can't be selected, an admin of your workspace has turned custom servers off.\n\nThe agent uses the Pyas tools that only read without asking. Before a tool that changes something, such as creating a test key, it shows a card for you to approve. To stop or remove the connection, open **Connectors**.\n\n## Ask for what you want\n\nAsk in plain words, for example:\n\n> Add a button that lets people connect their Google or Outlook calendar, and show their events for the next week.\n\nThe agent checks your setup with `setup_status` and gets the code for each task with `get_recipe`. It adds your app's preview address to the allowed origins with `update_app`, and creates a test key with `get_test_key`. You confirm the address in Pyas, from the link it gives you. Every response from Pyas ends with the next step, so the agent knows what to do next.\n\n## Store the key\n\nStore the test key as a secret named `PYAS_API_KEY`:\n\n1. In Lovable, open **More**, then **Settings**, then **Secrets**.\n2. Use the **Add secret** form. Don't paste the key into the chat.\n\nYour app's server code reads the secret, and it never reaches the browser. A test key works only in your app's [sandbox](\u002Fsandbox).\n\n## What Lovable adds\n\nLovable projects created since May 13, 2026 run on TanStack Start, and the code runs as server functions inside your app:\n\n```text\nsrc\u002Flib\u002Fpyas.server.ts                the Pyas client, on the server only\nsrc\u002Flib\u002Fpyas-connect.functions.ts     creates a connect session for the button\nsrc\u002Flib\u002Fpyas-events.functions.ts      the person's events for the next 7 days\nsrc\u002Flib\u002Fpyas-book.functions.ts        books the first free 30 minutes tomorrow\nsrc\u002Fcomponents\u002FConnectCalendar.tsx    the \"Connect an account\" button\n```\n\nYour pages call the functions directly, for example `await listEvents()`. The function that lists events looks like this:\n\n```ts [src\u002Flib\u002Fpyas-events.functions.ts]\nimport { createServerFn } from '@tanstack\u002Freact-start'\n\nconst DAY = 24 * 60 * 60 * 1000\n\nexport const listEvents = createServerFn({ method: 'GET' }).handler(async ({ context }) => {\n    \u002F\u002F Loaded here, on the server: this file is also part of the browser's code.\n    const { errorResult, pyas, signInRequired, subjectFor } = await import('.\u002Fpyas.server')\n    const subjectId = subjectFor(context)\n    if (!subjectId) return signInRequired()\n    try {\n        const now = Date.now()\n        const page = await pyas().events.list({\n            subjectId,\n            from: new Date(now).toISOString(),\n            to: new Date(now + 7 * DAY).toISOString(),\n            expand: true,\n            sort: 'start',\n            limit: 50,\n        })\n        \u002F\u002F Titles are written by whoever sent the invitation: show them as text, never as HTML.\n        return { events: page.data.map((event) => ({ id: event.id, title: event.title, startsAt: event.startsAt, endsAt: event.endsAt, isAllDay: event.isAllDay, provider: event.provider })) }\n    } catch (error) {\n        return errorResult(error)\n    }\n})\n```\n\nWith a test key, the functions use the sandbox's sample person, so the events and booking work right away. The connect button shows a message from Pyas that connecting a real calendar requires a live key. This is expected.\n\nIn an older Lovable project, built with React and Vite, the agent adds Supabase Edge Functions in `supabase\u002Ffunctions\u002F` instead, and your pages call them with `supabase.functions.invoke('pyas-events')`.\n\n## Receive webhooks\n\nAsk the agent to receive Pyas webhooks. It adds a public route, `src\u002Froutes\u002Fapi\u002Fpublic\u002Fpyas-webhooks.ts`, which verifies the signature of each delivery against the raw body before it reads the event.\n\nThe agent adds the endpoint to your sandbox with `create_webhook`, at your published address:\n\n```text\nhttps:\u002F\u002F\u003Cyour-app>.lovable.app\u002Fapi\u002Fpublic\u002Fpyas-webhooks\n```\n\nStore the signing secret that `create_webhook` returns as a secret named `PYAS_WEBHOOK_SECRET`. See [Webhooks](\u002Fwebhooks) for the events Pyas sends.\n\n## Go live\n\n1. Your app needs sign-in before people connect a calendar. Ask the agent to add sign-in, and to add it to the Pyas server functions. The functions then use the signed-in person. Until then, they respond with the code `SIGN_IN_REQUIRED` to every request made with a live key, so people's calendars never mix.\n2. In the Pyas dashboard, choose a plan and create a live key. Plans, payment and live keys are always done by you, in the dashboard.\n3. Replace the `PYAS_API_KEY` secret with the live key.\n4. Add your published address, such as `https:\u002F\u002Fyour-app.lovable.app`, to the app's allowed origins, and add a live webhook endpoint in the dashboard.\n",{"title":47,"description":48},"lovable",null,"_idt_MEP5EWFMJ15nLGto36RO0Rh3gjY9kHj3rLxcSY",1794697270293]