[{"data":1,"prerenderedAt":2233},["ShallowReactive",2],{"navigation":3,"page:\u002Fmigrating-from-v1":45,"v1-migration":1993},{"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":70},"guide","Migrating from v1","What changes when you move from v1 to v2 (the endpoint paths and the response format), and everything that stays the same, endpoint by endpoint.",[50,53,56,59,62,65,67],{"id":51,"title":52},"what-stays-the-same","What stays the same",{"id":54,"title":55},"what-changes","What changes",{"id":57,"title":58},"extended-properties","Extended properties",{"id":60,"title":61},"every-v1-endpoint-and-its-v2-equivalent","Every v1 endpoint and its v2 equivalent",{"id":63,"title":64},"v1-parameters-that-v2-still-accepts","v1 parameters that v2 still accepts",{"id":66,"title":28},"webhooks",{"id":68,"title":69},"a-plan-for-the-move","A plan for the move",{"id":71,"title":47,"body":72,"description":48,"extension":1983,"hero":1984,"meta":1985,"navigation":1986,"path":1987,"rawbody":1988,"sections":1989,"seo":1990,"stem":78,"v1":1991,"__hash__":1992},"docs\u002Fmigrating-from-v1.md",{"type":73,"value":74,"toc":1971},"minimark",[75,79,86,89,92,257,260,291,298,317,319,338,353,488,531,553,568,570,591,594,622,625,642,647,661,688,836,853,904,1003,1007,1010,1032,1100,1523,1540,1592,1622,1625,1750,1757,1818,1821,1823,1827,1846,1859,1862,1864,1870,1884,1887,1890,1892,1899,1911,1919,1935,1937,1967],[76,77,47],"h1",{"id":78},"migrating-from-v1",[80,81,82],"lead",{},[83,84,85],"p",{},"To move to v2, you change the paths you call and how you read the responses. Your API keys, your users' connected accounts, the ids you've stored and your webhook endpoints all carry over. Your users don't need to reconnect, and you don't need to register anything again.",[83,87,88],{},"You can migrate one endpoint at a time. v1 keeps working, and v1 and v2 requests count toward the same rate limit, so running both side by side doesn't cost anything extra.",[90,91,52],"h2",{"id":51},[93,94,95,108],"table",{},[96,97,98],"thead",{},[99,100,101,105],"tr",{},[102,103,104],"th",{},"What you have",[102,106,107],{},"In v2",[109,110,111,124,136,150,158,178,186,198,226,237],"tbody",{},[99,112,113,121],{},[114,115,116,117],"td",{},"API keys, sent in ",[118,119,120],"code",{},"x-api-key",[114,122,123],{},"The same keys in the same header",[99,125,126,133],{},[114,127,128,129,132],{},"Stored ",[118,130,131],{},"accountId","s",[114,134,135],{},"The same ids. v2 reads the same accounts.",[99,137,138,144],{},[114,139,140,141],{},"Stored calendar ids, including ",[118,142,143],{},"primary",[114,145,146,147],{},"Accepted as ",[118,148,149],{},"calendarRef",[99,151,152,155],{},[114,153,154],{},"Stored event ids",[114,156,157],{},"The same ids, returned and accepted",[99,159,160,163],{},[114,161,162],{},"Users connected through v1",[114,164,165,166,169,170,177],{},"Available in v2 right away. One exception: v1 didn't ask Google for calendar settings, so before you can read a Google account's settings (",[118,167,168],{},"GET \u002Fv2\u002Faccounts\u002F:accountId\u002Fsettings","), its user reconnects once. Until then that request returns ",[171,172,174],"a",{"href":173},"\u002Ferrors\u002FCONNECTION_SCOPE_INSUFFICIENT",[118,175,176],{},"CONNECTION_SCOPE_INSUFFICIENT",".",[99,179,180,183],{},[114,181,182],{},"Connecting accounts with PyasConnect or the v1 endpoints",[114,184,185],{},"Unchanged and still working. v2 also has its own endpoints for the same two steps, listed below this table.",[99,187,188,191],{},[114,189,190],{},"Webhook endpoints, their triggers and credentials",[114,192,193,194,177],{},"Kept. They still receive v1 payloads until you ",[171,195,197],{"href":196},"#webhooks","switch each one",[99,199,200,214],{},[114,201,202,203,206,207,206,210,213],{},"v1 query parameters (",[118,204,205],{},"calendarId",", ",[118,208,209],{},"timeMin",[118,211,212],{},"startDate","...)",[114,215,216,217,220,221,225],{},"Accepted, and listed in a ",[118,218,219],{},"Pyas-Deprecated-Fields"," header (",[171,222,224],{"href":223},"#v1-parameters-that-v2-still-accepts","see the full list",")",[99,227,228,234],{},[114,229,230,231,225],{},"The v1 request body (",[118,232,233],{},"{ accountId, calendarId, eventData }",[114,235,236],{},"Accepted as is",[99,238,239,245],{},[114,240,241,242],{},"Extended properties you write in ",[118,243,244],{},"eventData.meta",[114,246,247,248,251,252,256],{},"Still written when you send the v1 request body. Move them to ",[118,249,250],{},"metadata"," when it suits you (",[171,253,255],{"href":254},"#extended-properties","see how",").",[83,258,259],{},"These are the v1 endpoints for connecting accounts:",[261,262,267],"pre",{"className":263,"code":264,"language":265,"meta":266,"style":266},"language-http shiki shiki-themes css-variables","GET \u002F{provider}\u002Foauth\nPOST \u002F{provider}\u002Fconnect\n","http","",[118,268,269,282],{"__ignoreMap":266},[270,271,274,278],"span",{"class":272,"line":273},"line",1,[270,275,277],{"class":276},"sag7P","GET",[270,279,281],{"class":280},"sqSWz"," \u002F{provider}\u002Foauth\n",[270,283,285,288],{"class":272,"line":284},2,[270,286,287],{"class":276},"POST",[270,289,290],{"class":280}," \u002F{provider}\u002Fconnect\n",[83,292,293,294,177],{},"In v2, the same two steps are these endpoints. See ",[171,295,297],{"href":296},"\u002Fconnect#option-2-your-own-screens","Connecting accounts",[261,299,301],{"className":263,"code":300,"language":265,"meta":266,"style":266},"GET \u002Fv2\u002Fconnect\u002Foauth?provider=\nPOST \u002Fv2\u002Fconnect\u002Faccounts\n",[118,302,303,310],{"__ignoreMap":266},[270,304,305,307],{"class":272,"line":273},[270,306,277],{"class":276},[270,308,309],{"class":280}," \u002Fv2\u002Fconnect\u002Foauth?provider=\n",[270,311,312,314],{"class":272,"line":284},[270,313,287],{"class":276},[270,315,316],{"class":280}," \u002Fv2\u002Fconnect\u002Faccounts\n",[90,318,55],{"id":54},[83,320,321,325,326,329,330,333,334,337],{},[322,323,324],"strong",{},"The path."," v2 paths don't include the provider. ",[118,327,328],{},"\u002Fgoogle\u002Fevents"," and ",[118,331,332],{},"\u002Fmicrosoft\u002Fevents"," both become ",[118,335,336],{},"\u002Fv2\u002Fevents",", and the API determines each account's provider for you.",[83,339,340,343,344,348,349,352],{},[322,341,342],{},"The response format."," v1 returned Google's or Microsoft's own objects. v2 returns a single ",[171,345,347],{"href":346},"\u002Fevents","event format"," for every provider, so you need one parser instead of two. While you migrate, you can add ",[118,350,351],{},"include=raw"," to include the provider's original object as well, so you can still read every field you read today.",[93,354,355,368],{},[96,356,357],{},[99,358,359,362,365],{},[102,360,361],{},"Google (v1)",[102,363,364],{},"Microsoft (v1)",[102,366,367],{},"v2",[109,369,370,387,415,433,450,470],{},[99,371,372,377,382],{},[114,373,374],{},[118,375,376],{},"summary",[114,378,379],{},[118,380,381],{},"subject",[114,383,384],{},[118,385,386],{},"title",[99,388,389,397,404],{},[114,390,391,206,394],{},[118,392,393],{},"start.dateTime",[118,395,396],{},"start.date",[114,398,399,206,401],{},[118,400,393],{},[118,402,403],{},"isAllDay",[114,405,406,409,410,206,413],{},[118,407,408],{},"startsAt"," (UTC), ",[118,411,412],{},"startsAtLocal",[118,414,403],{},[99,416,417,422,427],{},[114,418,419],{},[118,420,421],{},"start.timeZone",[114,423,424,426],{},[118,425,421],{}," (Windows names)",[114,428,429,432],{},[118,430,431],{},"timezone"," (always IANA)",[99,434,435,440,445],{},[114,436,437],{},[118,438,439],{},"attendees[].responseStatus",[114,441,442],{},[118,443,444],{},"attendees[].status.response",[114,446,447],{},[118,448,449],{},"attendees[].response",[99,451,452,460,465],{},[114,453,454,206,457],{},[118,455,456],{},"hangoutLink",[118,458,459],{},"conferenceData",[114,461,462],{},[118,463,464],{},"onlineMeeting",[114,466,467],{},[118,468,469],{},"conferencing.joinUrl",[99,471,472,478,483],{},[114,473,474,477],{},[118,475,476],{},"recurrence"," (a list of rules)",[114,479,480,482],{},[118,481,476],{}," (a pattern object)",[114,484,485,487],{},[118,486,476],{}," (one RRULE)",[83,489,490,493,494,497,498,500,501,503,504,503,507,510,511,514,515,519,520,523,524,526,527,530],{},[322,491,492],{},"Errors."," In v1, ",[118,495,496],{},"error"," was either text or the provider's error. In v2, ",[118,499,496],{}," is always an object with a ",[118,502,118],{},", a ",[118,505,506],{},"message",[118,508,509],{},"hint"," and a ",[118,512,513],{},"traceId",". See ",[171,516,518],{"href":517},"\u002Ferrors","Errors"," for the full format. Existing code that checks ",[118,521,522],{},"success"," works without changes, but code that prints ",[118,525,496],{}," should print ",[118,528,529],{},"error.message"," instead.",[83,532,533,536,537,540,541,514,544,548,549,552],{},[322,534,535],{},"Pagination."," List responses include ",[118,538,539],{},"meta.nextCursor",", which you send in your next request as ",[118,542,543],{},"cursor",[171,545,547],{"href":546},"\u002Fpagination","Pagination",". The API still accepts a v1 ",[118,550,551],{},"nextPageToken"," from a Google response.",[83,554,555,558,559,563,564,567],{},[322,556,557],{},"Test keys."," v2 has a ",[171,560,562],{"href":561},"\u002Fsandbox","sandbox"," that you access with test keys (",[118,565,566],{},"pyas_sk_test_...","). Test every v2 request there before you switch production.",[90,569,58],{"id":57},[83,571,572,573,575,576,578,579,329,582,585,586,590],{},"In v1 you stored your own data on events as the provider's extended properties, in ",[118,574,244],{},". In v2 you store it in ",[118,577,250],{},". Metadata works the same on Google and Outlook, holds text, numbers, ",[118,580,581],{},"true",[118,583,584],{},"false",", and lists of those, and you can ",[171,587,589],{"href":588},"\u002Fevents#your-own-data-on-events","search for events by it"," across your whole app.",[83,592,593],{},"While you move:",[595,596,597,601,610],"ul",{},[598,599,600],"li",{},"Requests that send the v1 request body keep writing extended properties exactly as v1 did.",[598,602,603,604,607,608,530],{},"The v2 request body doesn't accept extended properties in ",[118,605,606],{},"providerOptions",". Use ",[118,609,250],{},[598,611,612,329,615,618,619,177],{},[118,613,614],{},"privateExtendedProperty",[118,616,617],{},"sharedExtendedProperty"," still find Google events, as separate parameters or inside ",[118,620,621],{},"filters",[83,623,624],{},"Responses to requests that still use extended properties include this header:",[261,626,628],{"className":263,"code":627,"language":265,"meta":266,"style":266},"Pyas-Deprecated-Fields: meta extended properties -> metadata\n",[118,629,630],{"__ignoreMap":266},[270,631,632,635,638],{"class":272,"line":273},[270,633,219],{"class":634},"sk1Au",[270,636,637],{"class":276},":",[270,639,641],{"class":640},"sTB4W"," meta extended properties -> metadata\n",[643,644,646],"h3",{"id":645},"read-them","Read them",[83,648,649,650,653,654,329,657,660],{},"Add ",[118,651,652],{},"include=extendedProperties"," to a request that reads events. On Google, you get Google's ",[118,655,656],{},"private",[118,658,659],{},"shared"," values:",[261,662,666],{"className":663,"code":664,"language":665,"meta":266,"style":266},"language-bash shiki shiki-themes css-variables","curl \"https:\u002F\u002Fapi.pyas.io\u002Fv2\u002Fevents\u002FEVENT_ID?accountId=ACCOUNT_ID&include=extendedProperties\" \\\n  -H \"x-api-key: $PYAS_API_KEY\"\n","bash",[118,667,668,680],{"__ignoreMap":266},[270,669,670,674,677],{"class":272,"line":273},[270,671,673],{"class":672},"s1IGz","curl",[270,675,676],{"class":634}," \"https:\u002F\u002Fapi.pyas.io\u002Fv2\u002Fevents\u002FEVENT_ID?accountId=ACCOUNT_ID&include=extendedProperties\"",[270,678,679],{"class":280}," \\\n",[270,681,682,685],{"class":272,"line":284},[270,683,684],{"class":640},"  -H",[270,686,687],{"class":634}," \"x-api-key: $PYAS_API_KEY\"\n",[261,689,693],{"className":690,"code":691,"language":692,"meta":266,"style":266},"language-json shiki shiki-themes css-variables","{\n  \"success\": true,\n  \"data\": {\n    \"id\": \"EVENT_ID\",\n    \"title\": \"Dinner with Sam\",\n    \"extendedProperties\": {\n      \"private\": { \"orderId\": \"A-1007\", \"stage\": \"paid\" },\n      \"shared\": { \"room\": \"4\" }\n    }\n  }\n}\n","json",[118,694,695,700,715,726,739,752,762,797,818,824,830],{"__ignoreMap":266},[270,696,697],{"class":272,"line":273},[270,698,699],{"class":280},"{\n",[270,701,702,705,708,712],{"class":272,"line":284},[270,703,704],{"class":276},"  \"success\"",[270,706,637],{"class":707},"sEEt9",[270,709,711],{"class":710},"sXULy"," true",[270,713,714],{"class":707},",\n",[270,716,718,721,723],{"class":272,"line":717},3,[270,719,720],{"class":276},"  \"data\"",[270,722,637],{"class":707},[270,724,725],{"class":280}," {\n",[270,727,729,732,734,737],{"class":272,"line":728},4,[270,730,731],{"class":276},"    \"id\"",[270,733,637],{"class":707},[270,735,736],{"class":634}," \"EVENT_ID\"",[270,738,714],{"class":707},[270,740,742,745,747,750],{"class":272,"line":741},5,[270,743,744],{"class":276},"    \"title\"",[270,746,637],{"class":707},[270,748,749],{"class":634}," \"Dinner with Sam\"",[270,751,714],{"class":707},[270,753,755,758,760],{"class":272,"line":754},6,[270,756,757],{"class":276},"    \"extendedProperties\"",[270,759,637],{"class":707},[270,761,725],{"class":280},[270,763,765,768,770,773,776,778,781,784,787,789,792,795],{"class":272,"line":764},7,[270,766,767],{"class":276},"      \"private\"",[270,769,637],{"class":707},[270,771,772],{"class":280}," { ",[270,774,775],{"class":276},"\"orderId\"",[270,777,637],{"class":707},[270,779,780],{"class":634}," \"A-1007\"",[270,782,783],{"class":707},",",[270,785,786],{"class":276}," \"stage\"",[270,788,637],{"class":707},[270,790,791],{"class":634}," \"paid\"",[270,793,794],{"class":280}," }",[270,796,714],{"class":707},[270,798,800,803,805,807,810,812,815],{"class":272,"line":799},8,[270,801,802],{"class":276},"      \"shared\"",[270,804,637],{"class":707},[270,806,772],{"class":280},[270,808,809],{"class":276},"\"room\"",[270,811,637],{"class":707},[270,813,814],{"class":634}," \"4\"",[270,816,817],{"class":280}," }\n",[270,819,821],{"class":272,"line":820},9,[270,822,823],{"class":280},"    }\n",[270,825,827],{"class":272,"line":826},10,[270,828,829],{"class":280},"  }\n",[270,831,833],{"class":272,"line":832},11,[270,834,835],{"class":280},"}\n",[83,837,838,839,842,843,845,846,848,849,852],{},"On Outlook, name the properties in ",[118,840,841],{},"extendedPropertyIds",", separated by commas (up to 10), because Outlook returns them only by ID. Each comes back under its name, as a number, ",[118,844,581],{}," or ",[118,847,584],{},", or text, depending on its type. Multi-value properties come back as lists, and a property without a name (such as ",[118,850,851],{},"Integer 0x6801",") comes back under its ID:",[261,854,856],{"className":663,"code":855,"language":665,"meta":266,"style":266},"curl -G \"https:\u002F\u002Fapi.pyas.io\u002Fv2\u002Fevents\u002FEVENT_ID\" \\\n  --data-urlencode \"accountId=ACCOUNT_ID\" \\\n  --data-urlencode \"include=extendedProperties\" \\\n  --data-urlencode \"extendedPropertyIds=String {66f5a359-4659-4830-9070-00047ec6ac6e} Name orderId,IntegerArray {66f5a359-4659-4830-9070-00047ec6ac6e} Name counts\" \\\n  -H \"x-api-key: $PYAS_API_KEY\"\n",[118,857,858,870,880,889,898],{"__ignoreMap":266},[270,859,860,862,865,868],{"class":272,"line":273},[270,861,673],{"class":672},[270,863,864],{"class":640}," -G",[270,866,867],{"class":634}," \"https:\u002F\u002Fapi.pyas.io\u002Fv2\u002Fevents\u002FEVENT_ID\"",[270,869,679],{"class":280},[270,871,872,875,878],{"class":272,"line":284},[270,873,874],{"class":640},"  --data-urlencode",[270,876,877],{"class":634}," \"accountId=ACCOUNT_ID\"",[270,879,679],{"class":280},[270,881,882,884,887],{"class":272,"line":717},[270,883,874],{"class":640},[270,885,886],{"class":634}," \"include=extendedProperties\"",[270,888,679],{"class":280},[270,890,891,893,896],{"class":272,"line":728},[270,892,874],{"class":640},[270,894,895],{"class":634}," \"extendedPropertyIds=String {66f5a359-4659-4830-9070-00047ec6ac6e} Name orderId,IntegerArray {66f5a359-4659-4830-9070-00047ec6ac6e} Name counts\"",[270,897,679],{"class":280},[270,899,900,902],{"class":272,"line":741},[270,901,684],{"class":640},[270,903,687],{"class":634},[261,905,907],{"className":690,"code":906,"language":692,"meta":266,"style":266},"{\n  \"success\": true,\n  \"data\": {\n    \"id\": \"EVENT_ID\",\n    \"title\": \"Dinner with Sam\",\n    \"extendedProperties\": {\n      \"orderId\": \"A-1007\",\n      \"counts\": [3, 4]\n    }\n  }\n}\n",[118,908,909,913,923,931,941,951,959,970,991,995,999],{"__ignoreMap":266},[270,910,911],{"class":272,"line":273},[270,912,699],{"class":280},[270,914,915,917,919,921],{"class":272,"line":284},[270,916,704],{"class":276},[270,918,637],{"class":707},[270,920,711],{"class":710},[270,922,714],{"class":707},[270,924,925,927,929],{"class":272,"line":717},[270,926,720],{"class":276},[270,928,637],{"class":707},[270,930,725],{"class":280},[270,932,933,935,937,939],{"class":272,"line":728},[270,934,731],{"class":276},[270,936,637],{"class":707},[270,938,736],{"class":634},[270,940,714],{"class":707},[270,942,943,945,947,949],{"class":272,"line":741},[270,944,744],{"class":276},[270,946,637],{"class":707},[270,948,749],{"class":634},[270,950,714],{"class":707},[270,952,953,955,957],{"class":272,"line":754},[270,954,757],{"class":276},[270,956,637],{"class":707},[270,958,725],{"class":280},[270,960,961,964,966,968],{"class":272,"line":764},[270,962,963],{"class":276},"      \"orderId\"",[270,965,637],{"class":707},[270,967,780],{"class":634},[270,969,714],{"class":707},[270,971,972,975,977,980,983,985,988],{"class":272,"line":799},[270,973,974],{"class":276},"      \"counts\"",[270,976,637],{"class":707},[270,978,979],{"class":280}," [",[270,981,982],{"class":710},"3",[270,984,783],{"class":707},[270,986,987],{"class":710}," 4",[270,989,990],{"class":280},"]\n",[270,992,993],{"class":272,"line":820},[270,994,823],{"class":280},[270,996,997],{"class":272,"line":826},[270,998,829],{"class":280},[270,1000,1001],{"class":272,"line":832},[270,1002,835],{"class":280},[643,1004,1006],{"id":1005},"copy-them-into-metadata","Copy them into metadata",[83,1008,1009],{},"The import copies extended properties into metadata for the accounts you choose. It only reads the calendars and doesn't change them, so your v1 code keeps working while it runs.",[1011,1012,1013],"ol",{},[598,1014,1015,1016,1019,1020,1023,1024,329,1026,1028,1029,1031],{},"Check what the import would copy. Send ",[118,1017,1018],{},"dryRun: true",", which reads a sample of recent events and writes nothing. For Google, the response lists every key it found, with examples and the scopes it was found in. ",[118,1021,1022],{},"conflicts"," counts the events where a key held different values in ",[118,1025,656],{},[118,1027,659],{},". In that case, the ",[118,1030,656],{}," value is the one imported. For Outlook, name each property by its ID. A property found on 0 events usually means a mistyped ID.",[261,1033,1035],{"className":663,"code":1034,"language":665,"meta":266,"style":266},"curl -X POST \"https:\u002F\u002Fapi.pyas.io\u002Fv2\u002Fmetadata\u002Fimport\" \\\n  -H \"x-api-key: $PYAS_API_KEY\" -H \"Content-Type: application\u002Fjson\" \\\n  -d '{\n    \"allAccounts\": true,\n    \"microsoft\": { \"properties\": [\"String {66f5a359-4659-4830-9070-00047ec6ac6e} Name orderId\"] },\n    \"convert\": \"auto\",\n    \"dryRun\": true\n  }'\n",[118,1036,1037,1052,1067,1075,1080,1085,1090,1095],{"__ignoreMap":266},[270,1038,1039,1041,1044,1047,1050],{"class":272,"line":273},[270,1040,673],{"class":672},[270,1042,1043],{"class":640}," -X",[270,1045,1046],{"class":640}," POST",[270,1048,1049],{"class":634}," \"https:\u002F\u002Fapi.pyas.io\u002Fv2\u002Fmetadata\u002Fimport\"",[270,1051,679],{"class":280},[270,1053,1054,1056,1059,1062,1065],{"class":272,"line":284},[270,1055,684],{"class":640},[270,1057,1058],{"class":634}," \"x-api-key: $PYAS_API_KEY\"",[270,1060,1061],{"class":640}," -H",[270,1063,1064],{"class":634}," \"Content-Type: application\u002Fjson\"",[270,1066,679],{"class":280},[270,1068,1069,1072],{"class":272,"line":717},[270,1070,1071],{"class":640},"  -d",[270,1073,1074],{"class":634}," '{\n",[270,1076,1077],{"class":272,"line":728},[270,1078,1079],{"class":634},"    \"allAccounts\": true,\n",[270,1081,1082],{"class":272,"line":741},[270,1083,1084],{"class":634},"    \"microsoft\": { \"properties\": [\"String {66f5a359-4659-4830-9070-00047ec6ac6e} Name orderId\"] },\n",[270,1086,1087],{"class":272,"line":754},[270,1088,1089],{"class":634},"    \"convert\": \"auto\",\n",[270,1091,1092],{"class":272,"line":764},[270,1093,1094],{"class":634},"    \"dryRun\": true\n",[270,1096,1097],{"class":272,"line":799},[270,1098,1099],{"class":634},"  }'\n",[261,1101,1103],{"className":690,"code":1102,"language":692,"meta":266,"style":266},"{\n  \"success\": true,\n  \"data\": {\n    \"dryRun\": true,\n    \"sampled\": { \"accounts\": 2, \"accountsInAll\": 2, \"calendars\": 2, \"events\": 214 },\n    \"google\": {\n      \"keys\": [\n        { \"key\": \"orderId\", \"events\": 96, \"examples\": [\"A-1007\", \"A-1012\"], \"scopes\": [\"private\"], \"conflicts\": 0, \"canBeMetadataKey\": true },\n        { \"key\": \"stage\", \"events\": 41, \"examples\": [\"paid\", \"draft\"], \"scopes\": [\"private\", \"shared\"], \"conflicts\": 3, \"canBeMetadataKey\": true }\n      ]\n    },\n    \"microsoft\": {\n      \"properties\": [\n        { \"id\": \"String {66f5a359-4659-4830-9070-00047ec6ac6e} Name orderId\", \"key\": \"orderId\", \"events\": 112, \"examples\": [\"B-220\", \"B-231\"] }\n      ]\n    },\n    \"wouldCopy\": 208,\n    \"unchanged\": 0,\n    \"wouldSkip\": 0,\n    \"skipped\": [],\n    \"errors\": []\n  }\n}\n",[118,1104,1105,1109,1119,1127,1138,1187,1196,1206,1285,1358,1363,1370,1380,1390,1440,1445,1452,1465,1477,1489,1502,1513,1518],{"__ignoreMap":266},[270,1106,1107],{"class":272,"line":273},[270,1108,699],{"class":280},[270,1110,1111,1113,1115,1117],{"class":272,"line":284},[270,1112,704],{"class":276},[270,1114,637],{"class":707},[270,1116,711],{"class":710},[270,1118,714],{"class":707},[270,1120,1121,1123,1125],{"class":272,"line":717},[270,1122,720],{"class":276},[270,1124,637],{"class":707},[270,1126,725],{"class":280},[270,1128,1129,1132,1134,1136],{"class":272,"line":728},[270,1130,1131],{"class":276},"    \"dryRun\"",[270,1133,637],{"class":707},[270,1135,711],{"class":710},[270,1137,714],{"class":707},[270,1139,1140,1143,1145,1147,1150,1152,1155,1157,1160,1162,1164,1166,1169,1171,1173,1175,1178,1180,1183,1185],{"class":272,"line":741},[270,1141,1142],{"class":276},"    \"sampled\"",[270,1144,637],{"class":707},[270,1146,772],{"class":280},[270,1148,1149],{"class":276},"\"accounts\"",[270,1151,637],{"class":707},[270,1153,1154],{"class":710}," 2",[270,1156,783],{"class":707},[270,1158,1159],{"class":276}," \"accountsInAll\"",[270,1161,637],{"class":707},[270,1163,1154],{"class":710},[270,1165,783],{"class":707},[270,1167,1168],{"class":276}," \"calendars\"",[270,1170,637],{"class":707},[270,1172,1154],{"class":710},[270,1174,783],{"class":707},[270,1176,1177],{"class":276}," \"events\"",[270,1179,637],{"class":707},[270,1181,1182],{"class":710}," 214",[270,1184,794],{"class":280},[270,1186,714],{"class":707},[270,1188,1189,1192,1194],{"class":272,"line":754},[270,1190,1191],{"class":276},"    \"google\"",[270,1193,637],{"class":707},[270,1195,725],{"class":280},[270,1197,1198,1201,1203],{"class":272,"line":764},[270,1199,1200],{"class":276},"      \"keys\"",[270,1202,637],{"class":707},[270,1204,1205],{"class":280}," [\n",[270,1207,1208,1211,1214,1216,1219,1221,1223,1225,1228,1230,1233,1235,1237,1240,1242,1245,1248,1250,1253,1255,1257,1260,1262,1264,1267,1269,1272,1274,1277,1279,1281,1283],{"class":272,"line":799},[270,1209,1210],{"class":280},"        { ",[270,1212,1213],{"class":276},"\"key\"",[270,1215,637],{"class":707},[270,1217,1218],{"class":634}," \"orderId\"",[270,1220,783],{"class":707},[270,1222,1177],{"class":276},[270,1224,637],{"class":707},[270,1226,1227],{"class":710}," 96",[270,1229,783],{"class":707},[270,1231,1232],{"class":276}," \"examples\"",[270,1234,637],{"class":707},[270,1236,979],{"class":280},[270,1238,1239],{"class":634},"\"A-1007\"",[270,1241,783],{"class":707},[270,1243,1244],{"class":634}," \"A-1012\"",[270,1246,1247],{"class":280},"]",[270,1249,783],{"class":707},[270,1251,1252],{"class":276}," \"scopes\"",[270,1254,637],{"class":707},[270,1256,979],{"class":280},[270,1258,1259],{"class":634},"\"private\"",[270,1261,1247],{"class":280},[270,1263,783],{"class":707},[270,1265,1266],{"class":276}," \"conflicts\"",[270,1268,637],{"class":707},[270,1270,1271],{"class":710}," 0",[270,1273,783],{"class":707},[270,1275,1276],{"class":276}," \"canBeMetadataKey\"",[270,1278,637],{"class":707},[270,1280,711],{"class":710},[270,1282,794],{"class":280},[270,1284,714],{"class":707},[270,1286,1287,1289,1291,1293,1295,1297,1299,1301,1304,1306,1308,1310,1312,1315,1317,1320,1322,1324,1326,1328,1330,1332,1334,1337,1339,1341,1343,1345,1348,1350,1352,1354,1356],{"class":272,"line":820},[270,1288,1210],{"class":280},[270,1290,1213],{"class":276},[270,1292,637],{"class":707},[270,1294,786],{"class":634},[270,1296,783],{"class":707},[270,1298,1177],{"class":276},[270,1300,637],{"class":707},[270,1302,1303],{"class":710}," 41",[270,1305,783],{"class":707},[270,1307,1232],{"class":276},[270,1309,637],{"class":707},[270,1311,979],{"class":280},[270,1313,1314],{"class":634},"\"paid\"",[270,1316,783],{"class":707},[270,1318,1319],{"class":634}," \"draft\"",[270,1321,1247],{"class":280},[270,1323,783],{"class":707},[270,1325,1252],{"class":276},[270,1327,637],{"class":707},[270,1329,979],{"class":280},[270,1331,1259],{"class":634},[270,1333,783],{"class":707},[270,1335,1336],{"class":634}," \"shared\"",[270,1338,1247],{"class":280},[270,1340,783],{"class":707},[270,1342,1266],{"class":276},[270,1344,637],{"class":707},[270,1346,1347],{"class":710}," 3",[270,1349,783],{"class":707},[270,1351,1276],{"class":276},[270,1353,637],{"class":707},[270,1355,711],{"class":710},[270,1357,817],{"class":280},[270,1359,1360],{"class":272,"line":826},[270,1361,1362],{"class":280},"      ]\n",[270,1364,1365,1368],{"class":272,"line":832},[270,1366,1367],{"class":280},"    }",[270,1369,714],{"class":707},[270,1371,1373,1376,1378],{"class":272,"line":1372},12,[270,1374,1375],{"class":276},"    \"microsoft\"",[270,1377,637],{"class":707},[270,1379,725],{"class":280},[270,1381,1383,1386,1388],{"class":272,"line":1382},13,[270,1384,1385],{"class":276},"      \"properties\"",[270,1387,637],{"class":707},[270,1389,1205],{"class":280},[270,1391,1393,1395,1398,1400,1403,1405,1408,1410,1412,1414,1416,1418,1421,1423,1425,1427,1429,1432,1434,1437],{"class":272,"line":1392},14,[270,1394,1210],{"class":280},[270,1396,1397],{"class":276},"\"id\"",[270,1399,637],{"class":707},[270,1401,1402],{"class":634}," \"String {66f5a359-4659-4830-9070-00047ec6ac6e} Name orderId\"",[270,1404,783],{"class":707},[270,1406,1407],{"class":276}," \"key\"",[270,1409,637],{"class":707},[270,1411,1218],{"class":634},[270,1413,783],{"class":707},[270,1415,1177],{"class":276},[270,1417,637],{"class":707},[270,1419,1420],{"class":710}," 112",[270,1422,783],{"class":707},[270,1424,1232],{"class":276},[270,1426,637],{"class":707},[270,1428,979],{"class":280},[270,1430,1431],{"class":634},"\"B-220\"",[270,1433,783],{"class":707},[270,1435,1436],{"class":634}," \"B-231\"",[270,1438,1439],{"class":280},"] }\n",[270,1441,1443],{"class":272,"line":1442},15,[270,1444,1362],{"class":280},[270,1446,1448,1450],{"class":272,"line":1447},16,[270,1449,1367],{"class":280},[270,1451,714],{"class":707},[270,1453,1455,1458,1460,1463],{"class":272,"line":1454},17,[270,1456,1457],{"class":276},"    \"wouldCopy\"",[270,1459,637],{"class":707},[270,1461,1462],{"class":710}," 208",[270,1464,714],{"class":707},[270,1466,1468,1471,1473,1475],{"class":272,"line":1467},18,[270,1469,1470],{"class":276},"    \"unchanged\"",[270,1472,637],{"class":707},[270,1474,1271],{"class":710},[270,1476,714],{"class":707},[270,1478,1480,1483,1485,1487],{"class":272,"line":1479},19,[270,1481,1482],{"class":276},"    \"wouldSkip\"",[270,1484,637],{"class":707},[270,1486,1271],{"class":710},[270,1488,714],{"class":707},[270,1490,1492,1495,1497,1500],{"class":272,"line":1491},20,[270,1493,1494],{"class":276},"    \"skipped\"",[270,1496,637],{"class":707},[270,1498,1499],{"class":280}," []",[270,1501,714],{"class":707},[270,1503,1505,1508,1510],{"class":272,"line":1504},21,[270,1506,1507],{"class":276},"    \"errors\"",[270,1509,637],{"class":707},[270,1511,1512],{"class":280}," []\n",[270,1514,1516],{"class":272,"line":1515},22,[270,1517,829],{"class":280},[270,1519,1521],{"class":272,"line":1520},23,[270,1522,835],{"class":280},[1011,1524,1525],{"start":284},[598,1526,1527,1528,1531,1532,1535,1536,1539],{},"Run the import. Send the same body without ",[118,1529,1530],{},"dryRun",". The response is ",[118,1533,1534],{},"202 Accepted"," with an ",[118,1537,1538],{},"importId",", and the import runs in the background.",[261,1541,1543],{"className":663,"code":1542,"language":665,"meta":266,"style":266},"curl -X POST \"https:\u002F\u002Fapi.pyas.io\u002Fv2\u002Fmetadata\u002Fimport\" \\\n  -H \"x-api-key: $PYAS_API_KEY\" -H \"Content-Type: application\u002Fjson\" \\\n  -d '{\n    \"allAccounts\": true,\n    \"microsoft\": { \"properties\": [\"String {66f5a359-4659-4830-9070-00047ec6ac6e} Name orderId\"] },\n    \"convert\": \"auto\"\n  }'\n",[118,1544,1545,1557,1569,1575,1579,1583,1588],{"__ignoreMap":266},[270,1546,1547,1549,1551,1553,1555],{"class":272,"line":273},[270,1548,673],{"class":672},[270,1550,1043],{"class":640},[270,1552,1046],{"class":640},[270,1554,1049],{"class":634},[270,1556,679],{"class":280},[270,1558,1559,1561,1563,1565,1567],{"class":272,"line":284},[270,1560,684],{"class":640},[270,1562,1058],{"class":634},[270,1564,1061],{"class":640},[270,1566,1064],{"class":634},[270,1568,679],{"class":280},[270,1570,1571,1573],{"class":272,"line":717},[270,1572,1071],{"class":640},[270,1574,1074],{"class":634},[270,1576,1577],{"class":272,"line":728},[270,1578,1079],{"class":634},[270,1580,1581],{"class":272,"line":741},[270,1582,1084],{"class":634},[270,1584,1585],{"class":272,"line":754},[270,1586,1587],{"class":634},"    \"convert\": \"auto\"\n",[270,1589,1590],{"class":272,"line":764},[270,1591,1099],{"class":634},[1011,1593,1594,1613],{"start":717},[598,1595,1596,1597,1600,1601,1604,1605,1608,1609,1612],{},"Check its progress with ",[118,1598,1599],{},"GET \u002Fv2\u002Fmetadata\u002Fimports\u002F{importId}"," until ",[118,1602,1603],{},"status"," is ",[118,1606,1607],{},"done",". ",[118,1610,1611],{},"counts.copied"," is the number of events that received metadata.",[598,1614,1615,1616,1618,1619,177],{},"Switch your code to read and write ",[118,1617,250],{},". Run the import again just before you stop writing extended properties, to copy anything written since the first run. Running it again is safe: an event that already holds the same values is left as it is, and values you've already set in v2 are kept unless you send ",[118,1620,1621],{},"\"overwrite\": true",[83,1623,1624],{},"How values arrive in metadata:",[93,1626,1627,1637],{},[96,1628,1629],{},[99,1630,1631,1634],{},[102,1632,1633],{},"Extended property",[102,1635,1636],{},"In metadata",[109,1638,1639,1652,1674,1688,1700,1727,1740],{},[99,1640,1641,1647],{},[114,1642,1643,1644],{},"Google ",[118,1645,1646],{},"private: { \"orderId\": \"A-1007\" }",[114,1648,1649],{},[118,1650,1651],{},"\"orderId\": \"A-1007\"",[99,1653,1654,1667],{},[114,1655,1656,1657,845,1660,1663,1664],{},"Google value ",[118,1658,1659],{},"\"42\"",[118,1661,1662],{},"\"true\"",", with ",[118,1665,1666],{},"\"convert\": \"auto\"",[114,1668,1669,845,1672],{},[118,1670,1671],{},"42",[118,1673,581],{},[99,1675,1676,1683],{},[114,1677,1656,1678,1663,1681],{},[118,1679,1680],{},"'[\"Food\",\"Hiking\"]'",[118,1682,1666],{},[114,1684,1685],{},[118,1686,1687],{},"[\"Food\", \"Hiking\"]",[99,1689,1690,1696],{},[114,1691,1692,1693],{},"Outlook ",[118,1694,1695],{},"String {...} Name orderId",[114,1697,1698],{},[118,1699,1651],{},[99,1701,1702,1714],{},[114,1703,1692,1704,206,1707,206,1710,1713],{},[118,1705,1706],{},"Integer",[118,1708,1709],{},"Double",[118,1711,1712],{},"Boolean"," and the other typed properties",[114,1715,1716,1717,1719,1720,1722,1723,1726],{},"A number or ",[118,1718,581],{},"\u002F",[118,1721,584],{},", whatever ",[118,1724,1725],{},"convert"," says",[99,1728,1729,1735],{},[114,1730,1692,1731,1734],{},[118,1732,1733],{},"StringArray {...} Name tags",", and the other multi-value types",[114,1736,1737],{},[118,1738,1739],{},"\"tags\": [\"Food\", \"Hiking\"]",[99,1741,1742,1747],{},[114,1743,1692,1744,1746],{},[118,1745,851],{},", a property without a name",[114,1748,1749],{},"The key you choose, as shown below",[83,1751,1752,1753,1756],{},"A property without a name needs a key. Send it in ",[118,1754,1755],{},"microsoft.properties"," with the key you want:",[261,1758,1760],{"className":690,"code":1759,"language":692,"meta":266,"style":266},"\"microsoft\": {\n  \"properties\": [\n    \"String {66f5a359-4659-4830-9070-00047ec6ac6e} Name orderId\",\n    { \"id\": \"Integer 0x6801\", \"key\": \"legacyCount\" }\n  ]\n}\n",[118,1761,1762,1770,1779,1786,1809,1814],{"__ignoreMap":266},[270,1763,1764,1767],{"class":272,"line":273},[270,1765,1766],{"class":634},"\"microsoft\"",[270,1768,1769],{"class":280},": {\n",[270,1771,1772,1775,1777],{"class":272,"line":284},[270,1773,1774],{"class":276},"  \"properties\"",[270,1776,637],{"class":707},[270,1778,1205],{"class":280},[270,1780,1781,1784],{"class":272,"line":717},[270,1782,1783],{"class":634},"    \"String {66f5a359-4659-4830-9070-00047ec6ac6e} Name orderId\"",[270,1785,714],{"class":707},[270,1787,1788,1791,1793,1795,1798,1800,1802,1804,1807],{"class":272,"line":728},[270,1789,1790],{"class":280},"    { ",[270,1792,1397],{"class":276},[270,1794,637],{"class":707},[270,1796,1797],{"class":634}," \"Integer 0x6801\"",[270,1799,783],{"class":707},[270,1801,1407],{"class":276},[270,1803,637],{"class":707},[270,1805,1806],{"class":634}," \"legacyCount\"",[270,1808,817],{"class":280},[270,1810,1811],{"class":272,"line":741},[270,1812,1813],{"class":280},"  ]\n",[270,1815,1816],{"class":272,"line":754},[270,1817,835],{"class":280},[83,1819,1820],{},"You can also rename a named property the same way. Each key holds up to 100 list items, and an event's metadata is at most 50 keys and 8 KB.",[90,1822,61],{"id":60},[1824,1825],"v1-migration",{"show":1826},"calls",[83,1828,1829,1830,1833,1834,1839,1840,1845],{},"Zoom's meeting endpoints (",[118,1831,1832],{},"\u002Fzoom\u002Fmeetings",") are ",[171,1835,1836],{"href":23},[118,1837,1838],{},"\u002Fv2\u002Fmeetings"," in v2. Deleting a connected account is ",[171,1841,1842],{"href":8},[118,1843,1844],{},"DELETE \u002Fv2\u002Faccounts\u002F{accountId}"," in v2. Deactivating one is still available only in v1:",[261,1847,1849],{"className":263,"code":1848,"language":265,"meta":266,"style":266},"PATCH \u002Faccounts\u002Fdeactivate\u002F:id\n",[118,1850,1851],{"__ignoreMap":266},[270,1852,1853,1856],{"class":272,"line":273},[270,1854,1855],{"class":276},"PATCH",[270,1857,1858],{"class":280}," \u002Faccounts\u002Fdeactivate\u002F:id\n",[83,1860,1861],{},"You can also deactivate an account from the dashboard. Connecting accounts through the v1 endpoints still works, and accounts connected that way are available in v2.",[90,1863,64],{"id":63},[83,1865,1866,1867,1869],{},"Each v2 endpoint below accepts the v1 query parameters and responds normally. The response lists the v1 parameters you used in a ",[118,1868,219],{}," header, so you can see which ones to rename when it's convenient. For example:",[261,1871,1873],{"className":263,"code":1872,"language":265,"meta":266,"style":266},"Pyas-Deprecated-Fields: timeZone (no effect), calendarId -> calendarRef\n",[118,1874,1875],{"__ignoreMap":266},[270,1876,1877,1879,1881],{"class":272,"line":273},[270,1878,219],{"class":634},[270,1880,637],{"class":276},[270,1882,1883],{"class":640}," timeZone (no effect), calendarId -> calendarRef\n",[83,1885,1886],{},"Times sent in v1 parameters without a time zone are read as UTC, the same as in v1.",[1824,1888],{"show":1889},"names",[90,1891,28],{"id":66},[83,1893,1894,1895,1898],{},"Endpoints you registered in v1 keep receiving v1 payloads. When your handler is ready for v2 payloads, switch each endpoint by sending ",[118,1896,1897],{},"{ \"payloadVersion\": \"v2\" }"," to:",[261,1900,1902],{"className":263,"code":1901,"language":265,"meta":266,"style":266},"PATCH \u002Fv2\u002Fwebhooks\u002F{webhookId}\n",[118,1903,1904],{"__ignoreMap":266},[270,1905,1906,1908],{"class":272,"line":273},[270,1907,1855],{"class":276},[270,1909,1910],{"class":280}," \u002Fv2\u002Fwebhooks\u002F{webhookId}\n",[83,1912,1913,1914,1918],{},"From then on, the endpoint receives v2 deliveries for the event types that match its v1 triggers. These deliveries are ",[171,1915,1917],{"href":1916},"\u002Fwebhooks#verify-every-delivery","signed",", retried for 41 hours, and can be replayed. The response includes the endpoint's signing secret.",[83,1920,1921,1922,1926,1927,1930,1931,1934],{},"You can also keep the v1 endpoint while you build your v2 handler: add a v2 endpoint beside it, at the same URL or a new one, and delete the v1 endpoint when you finish. While both exist, each change arrives in both formats. If one URL receives both, ",[171,1923,1925],{"href":1924},"\u002Fwebhooks#accept-both-formats-at-one-url","tell them apart by their headers",": a v2 delivery has a ",[118,1928,1929],{},"Pyas-Signature"," header, and a v1 delivery has ",[118,1932,1933],{},"x-pyas-webhook-event",". The endpoint's page in the dashboard shows the deliveries of either format.",[90,1936,69],{"id":68},[1011,1938,1939,1942,1952,1964],{},[598,1940,1941],{},"Create a test key and run your v2 requests against the sandbox.",[598,1943,1944,1945,1948,1949,1951],{},"Migrate reads first. Update the path and read ",[118,1946,1947],{},"data"," in the v2 format. Watch your logs for ",[118,1950,219],{}," and rename parameters when it's convenient.",[598,1953,1954,1955,1958,1959,1963],{},"Migrate writes. Your v1 request bodies work as they are. Switch to the v2 format, with an ",[118,1956,1957],{},"Idempotency-Key",", the next time you work on that code. If you write extended properties, ",[171,1960,1962],{"href":1961},"#copy-them-into-metadata","copy them into metadata"," first.",[598,1965,1966],{},"Move your webhook endpoints one at a time, and verify signatures as you go. Until an endpoint is on v2, its handler accepts v1 payloads, so a URL that receives both formats checks the headers of each request.",[1968,1969,1970],"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 .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 .sk1Au, html code.shiki .sk1Au{--shiki-default:var(--shiki-token-string-expression)}html pre.shiki code .sTB4W, html code.shiki .sTB4W{--shiki-default:var(--shiki-token-string)}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 pre.shiki code .sXULy, html code.shiki .sXULy{--shiki-default:var(--shiki-token-constant)}",{"title":266,"searchDepth":717,"depth":717,"links":1972},[1973,1974,1975,1979,1980,1981,1982],{"id":51,"depth":284,"text":52},{"id":54,"depth":284,"text":55},{"id":57,"depth":284,"text":58,"children":1976},[1977,1978],{"id":645,"depth":717,"text":646},{"id":1005,"depth":717,"text":1006},{"id":60,"depth":284,"text":61},{"id":63,"depth":284,"text":64},{"id":66,"depth":284,"text":28},{"id":68,"depth":284,"text":69},"md",false,{},true,"\u002Fmigrating-from-v1","---\ntitle: Migrating from v1\ndescription: What changes when you move from v1 to v2 (the endpoint paths and the response format), and everything that stays the same, endpoint by endpoint.\nv1: \u002Frest-api-reference\u002Fintroduction\n---\n\n# Migrating from v1\n\n::lead\nTo move to v2, you change the paths you call and how you read the responses. Your API keys, your users' connected accounts, the ids you've stored and your webhook endpoints all carry over. Your users don't need to reconnect, and you don't need to register anything again.\n::\n\nYou can migrate one endpoint at a time. v1 keeps working, and v1 and v2 requests count toward the same rate limit, so running both side by side doesn't cost anything extra.\n\n## What stays the same\n\n| What you have | In v2 |\n|---|---|\n| API keys, sent in `x-api-key` | The same keys in the same header |\n| Stored `accountId`s | The same ids. v2 reads the same accounts. |\n| Stored calendar ids, including `primary` | Accepted as `calendarRef` |\n| Stored event ids | The same ids, returned and accepted |\n| Users connected through v1 | Available in v2 right away. One exception: v1 didn't ask Google for calendar settings, so before you can read a Google account's settings (`GET \u002Fv2\u002Faccounts\u002F:accountId\u002Fsettings`), its user reconnects once. Until then that request returns [`CONNECTION_SCOPE_INSUFFICIENT`](\u002Ferrors\u002FCONNECTION_SCOPE_INSUFFICIENT). |\n| Connecting accounts with PyasConnect or the v1 endpoints | Unchanged and still working. v2 also has its own endpoints for the same two steps, listed below this table. |\n| Webhook endpoints, their triggers and credentials | Kept. They still receive v1 payloads until you [switch each one](#webhooks). |\n| v1 query parameters (`calendarId`, `timeMin`, `startDate`...) | Accepted, and listed in a `Pyas-Deprecated-Fields` header ([see the full list](#v1-parameters-that-v2-still-accepts)) |\n| The v1 request body (`{ accountId, calendarId, eventData }`) | Accepted as is |\n| Extended properties you write in `eventData.meta` | Still written when you send the v1 request body. Move them to `metadata` when it suits you ([see how](#extended-properties)). |\n\nThese are the v1 endpoints for connecting accounts:\n\n```http\nGET \u002F{provider}\u002Foauth\nPOST \u002F{provider}\u002Fconnect\n```\n\nIn v2, the same two steps are these endpoints. See [Connecting accounts](\u002Fconnect#option-2-your-own-screens).\n\n```http\nGET \u002Fv2\u002Fconnect\u002Foauth?provider=\nPOST \u002Fv2\u002Fconnect\u002Faccounts\n```\n\n## What changes\n\n**The path.** v2 paths don't include the provider. `\u002Fgoogle\u002Fevents` and `\u002Fmicrosoft\u002Fevents` both become `\u002Fv2\u002Fevents`, and the API determines each account's provider for you.\n\n**The response format.** v1 returned Google's or Microsoft's own objects. v2 returns a single [event format](\u002Fevents) for every provider, so you need one parser instead of two. While you migrate, you can add `include=raw` to include the provider's original object as well, so you can still read every field you read today.\n\n| Google (v1) | Microsoft (v1) | v2 |\n|---|---|---|\n| `summary` | `subject` | `title` |\n| `start.dateTime`, `start.date` | `start.dateTime`, `isAllDay` | `startsAt` (UTC), `startsAtLocal`, `isAllDay` |\n| `start.timeZone` | `start.timeZone` (Windows names) | `timezone` (always IANA) |\n| `attendees[].responseStatus` | `attendees[].status.response` | `attendees[].response` |\n| `hangoutLink`, `conferenceData` | `onlineMeeting` | `conferencing.joinUrl` |\n| `recurrence` (a list of rules) | `recurrence` (a pattern object) | `recurrence` (one RRULE) |\n\n**Errors.** In v1, `error` was either text or the provider's error. In v2, `error` is always an object with a `code`, a `message`, a `hint` and a `traceId`. See [Errors](\u002Ferrors) for the full format. Existing code that checks `success` works without changes, but code that prints `error` should print `error.message` instead.\n\n**Pagination.** List responses include `meta.nextCursor`, which you send in your next request as `cursor`. See [Pagination](\u002Fpagination). The API still accepts a v1 `nextPageToken` from a Google response.\n\n**Test keys.** v2 has a [sandbox](\u002Fsandbox) that you access with test keys (`pyas_sk_test_...`). Test every v2 request there before you switch production.\n\n## Extended properties\n\nIn v1 you stored your own data on events as the provider's extended properties, in `eventData.meta`. In v2 you store it in `metadata`. Metadata works the same on Google and Outlook, holds text, numbers, `true` and `false`, and lists of those, and you can [search for events by it](\u002Fevents#your-own-data-on-events) across your whole app.\n\nWhile you move:\n\n- Requests that send the v1 request body keep writing extended properties exactly as v1 did.\n- The v2 request body doesn't accept extended properties in `providerOptions`. Use `metadata` instead.\n- `privateExtendedProperty` and `sharedExtendedProperty` still find Google events, as separate parameters or inside `filters`.\n\nResponses to requests that still use extended properties include this header:\n\n```http\nPyas-Deprecated-Fields: meta extended properties -> metadata\n```\n\n### Read them\n\nAdd `include=extendedProperties` to a request that reads events. On Google, you get Google's `private` and `shared` values:\n\n```bash\ncurl \"https:\u002F\u002Fapi.pyas.io\u002Fv2\u002Fevents\u002FEVENT_ID?accountId=ACCOUNT_ID&include=extendedProperties\" \\\n  -H \"x-api-key: $PYAS_API_KEY\"\n```\n\n```json\n{\n  \"success\": true,\n  \"data\": {\n    \"id\": \"EVENT_ID\",\n    \"title\": \"Dinner with Sam\",\n    \"extendedProperties\": {\n      \"private\": { \"orderId\": \"A-1007\", \"stage\": \"paid\" },\n      \"shared\": { \"room\": \"4\" }\n    }\n  }\n}\n```\n\nOn Outlook, name the properties in `extendedPropertyIds`, separated by commas (up to 10), because Outlook returns them only by ID. Each comes back under its name, as a number, `true` or `false`, or text, depending on its type. Multi-value properties come back as lists, and a property without a name (such as `Integer 0x6801`) comes back under its ID:\n\n```bash\ncurl -G \"https:\u002F\u002Fapi.pyas.io\u002Fv2\u002Fevents\u002FEVENT_ID\" \\\n  --data-urlencode \"accountId=ACCOUNT_ID\" \\\n  --data-urlencode \"include=extendedProperties\" \\\n  --data-urlencode \"extendedPropertyIds=String {66f5a359-4659-4830-9070-00047ec6ac6e} Name orderId,IntegerArray {66f5a359-4659-4830-9070-00047ec6ac6e} Name counts\" \\\n  -H \"x-api-key: $PYAS_API_KEY\"\n```\n\n```json\n{\n  \"success\": true,\n  \"data\": {\n    \"id\": \"EVENT_ID\",\n    \"title\": \"Dinner with Sam\",\n    \"extendedProperties\": {\n      \"orderId\": \"A-1007\",\n      \"counts\": [3, 4]\n    }\n  }\n}\n```\n\n### Copy them into metadata\n\nThe import copies extended properties into metadata for the accounts you choose. It only reads the calendars and doesn't change them, so your v1 code keeps working while it runs.\n\n1. Check what the import would copy. Send `dryRun: true`, which reads a sample of recent events and writes nothing. For Google, the response lists every key it found, with examples and the scopes it was found in. `conflicts` counts the events where a key held different values in `private` and `shared`. In that case, the `private` value is the one imported. For Outlook, name each property by its ID. A property found on 0 events usually means a mistyped ID.\n\n```bash\ncurl -X POST \"https:\u002F\u002Fapi.pyas.io\u002Fv2\u002Fmetadata\u002Fimport\" \\\n  -H \"x-api-key: $PYAS_API_KEY\" -H \"Content-Type: application\u002Fjson\" \\\n  -d '{\n    \"allAccounts\": true,\n    \"microsoft\": { \"properties\": [\"String {66f5a359-4659-4830-9070-00047ec6ac6e} Name orderId\"] },\n    \"convert\": \"auto\",\n    \"dryRun\": true\n  }'\n```\n\n```json\n{\n  \"success\": true,\n  \"data\": {\n    \"dryRun\": true,\n    \"sampled\": { \"accounts\": 2, \"accountsInAll\": 2, \"calendars\": 2, \"events\": 214 },\n    \"google\": {\n      \"keys\": [\n        { \"key\": \"orderId\", \"events\": 96, \"examples\": [\"A-1007\", \"A-1012\"], \"scopes\": [\"private\"], \"conflicts\": 0, \"canBeMetadataKey\": true },\n        { \"key\": \"stage\", \"events\": 41, \"examples\": [\"paid\", \"draft\"], \"scopes\": [\"private\", \"shared\"], \"conflicts\": 3, \"canBeMetadataKey\": true }\n      ]\n    },\n    \"microsoft\": {\n      \"properties\": [\n        { \"id\": \"String {66f5a359-4659-4830-9070-00047ec6ac6e} Name orderId\", \"key\": \"orderId\", \"events\": 112, \"examples\": [\"B-220\", \"B-231\"] }\n      ]\n    },\n    \"wouldCopy\": 208,\n    \"unchanged\": 0,\n    \"wouldSkip\": 0,\n    \"skipped\": [],\n    \"errors\": []\n  }\n}\n```\n\n2. Run the import. Send the same body without `dryRun`. The response is `202 Accepted` with an `importId`, and the import runs in the background.\n\n```bash\ncurl -X POST \"https:\u002F\u002Fapi.pyas.io\u002Fv2\u002Fmetadata\u002Fimport\" \\\n  -H \"x-api-key: $PYAS_API_KEY\" -H \"Content-Type: application\u002Fjson\" \\\n  -d '{\n    \"allAccounts\": true,\n    \"microsoft\": { \"properties\": [\"String {66f5a359-4659-4830-9070-00047ec6ac6e} Name orderId\"] },\n    \"convert\": \"auto\"\n  }'\n```\n\n3. Check its progress with `GET \u002Fv2\u002Fmetadata\u002Fimports\u002F{importId}` until `status` is `done`. `counts.copied` is the number of events that received metadata.\n\n4. Switch your code to read and write `metadata`. Run the import again just before you stop writing extended properties, to copy anything written since the first run. Running it again is safe: an event that already holds the same values is left as it is, and values you've already set in v2 are kept unless you send `\"overwrite\": true`.\n\nHow values arrive in metadata:\n\n| Extended property | In metadata |\n|---|---|\n| Google `private: { \"orderId\": \"A-1007\" }` | `\"orderId\": \"A-1007\"` |\n| Google value `\"42\"` or `\"true\"`, with `\"convert\": \"auto\"` | `42` or `true` |\n| Google value `'[\"Food\",\"Hiking\"]'`, with `\"convert\": \"auto\"` | `[\"Food\", \"Hiking\"]` |\n| Outlook `String {...} Name orderId` | `\"orderId\": \"A-1007\"` |\n| Outlook `Integer`, `Double`, `Boolean` and the other typed properties | A number or `true`\u002F`false`, whatever `convert` says |\n| Outlook `StringArray {...} Name tags`, and the other multi-value types | `\"tags\": [\"Food\", \"Hiking\"]` |\n| Outlook `Integer 0x6801`, a property without a name | The key you choose, as shown below |\n\nA property without a name needs a key. Send it in `microsoft.properties` with the key you want:\n\n```json\n\"microsoft\": {\n  \"properties\": [\n    \"String {66f5a359-4659-4830-9070-00047ec6ac6e} Name orderId\",\n    { \"id\": \"Integer 0x6801\", \"key\": \"legacyCount\" }\n  ]\n}\n```\n\nYou can also rename a named property the same way. Each key holds up to 100 list items, and an event's metadata is at most 50 keys and 8 KB.\n\n## Every v1 endpoint and its v2 equivalent\n\n::v1-migration{show=\"calls\"}\n::\n\nZoom's meeting endpoints (`\u002Fzoom\u002Fmeetings`) are [`\u002Fv2\u002Fmeetings`](\u002Freference\u002Fmeetings) in v2. Deleting a connected account is [`DELETE \u002Fv2\u002Faccounts\u002F{accountId}`](\u002Freference\u002Faccounts) in v2. Deactivating one is still available only in v1:\n\n```http\nPATCH \u002Faccounts\u002Fdeactivate\u002F:id\n```\n\nYou can also deactivate an account from the dashboard. Connecting accounts through the v1 endpoints still works, and accounts connected that way are available in v2.\n\n## v1 parameters that v2 still accepts\n\nEach v2 endpoint below accepts the v1 query parameters and responds normally. The response lists the v1 parameters you used in a `Pyas-Deprecated-Fields` header, so you can see which ones to rename when it's convenient. For example:\n\n```http\nPyas-Deprecated-Fields: timeZone (no effect), calendarId -> calendarRef\n```\n\nTimes sent in v1 parameters without a time zone are read as UTC, the same as in v1.\n\n::v1-migration{show=\"names\"}\n::\n\n## Webhooks\n\nEndpoints you registered in v1 keep receiving v1 payloads. When your handler is ready for v2 payloads, switch each endpoint by sending `{ \"payloadVersion\": \"v2\" }` to:\n\n```http\nPATCH \u002Fv2\u002Fwebhooks\u002F{webhookId}\n```\n\nFrom then on, the endpoint receives v2 deliveries for the event types that match its v1 triggers. These deliveries are [signed](\u002Fwebhooks#verify-every-delivery), retried for 41 hours, and can be replayed. The response includes the endpoint's signing secret.\n\nYou can also keep the v1 endpoint while you build your v2 handler: add a v2 endpoint beside it, at the same URL or a new one, and delete the v1 endpoint when you finish. While both exist, each change arrives in both formats. If one URL receives both, [tell them apart by their headers](\u002Fwebhooks#accept-both-formats-at-one-url): a v2 delivery has a `Pyas-Signature` header, and a v1 delivery has `x-pyas-webhook-event`. The endpoint's page in the dashboard shows the deliveries of either format.\n\n## A plan for the move\n\n1. Create a test key and run your v2 requests against the sandbox.\n2. Migrate reads first. Update the path and read `data` in the v2 format. Watch your logs for `Pyas-Deprecated-Fields` and rename parameters when it's convenient.\n3. Migrate writes. Your v1 request bodies work as they are. Switch to the v2 format, with an `Idempotency-Key`, the next time you work on that code. If you write extended properties, [copy them into metadata](#copy-them-into-metadata) first.\n4. Move your webhook endpoints one at a time, and verify signatures as you go. Until an endpoint is on v2, its handler accepts v1 payloads, so a URL that receives both formats checks the headers of each request.\n",null,{"title":47,"description":48},"\u002Frest-api-reference\u002Fintroduction","DxuOIDJFfY9aqVa0a5vXTsSjPs0OHwAh8NzQYIenb94",[1994,2006,2018,2079,2091,2105,2116,2133,2150,2167,2177,2188,2199,2211,2222],{"operationId":1995,"summary":1996,"method":277,"path":1997,"href":1998,"replaces":1999,"renamed":2002,"ignored":2003,"refused":2004,"notes":2005},"calendars.list","List calendars","\u002Fv2\u002Fcalendars","\u002Freference\u002Fcalendars#calendars-list",[2000,2001],"GET \u002Fgoogle\u002Fcalendars","GET \u002Fmicrosoft\u002Fcalendars",[],[],[],[],{"operationId":2007,"summary":2008,"method":277,"path":2009,"href":2010,"replaces":2011,"renamed":2014,"ignored":2015,"refused":2016,"notes":2017},"calendars.get","Get a calendar","\u002Fv2\u002Fcalendars\u002F{calendarRef}","\u002Freference\u002Fcalendars#calendars-get",[2012,2013],"GET \u002Fgoogle\u002Fcalendars\u002F:id","GET \u002Fmicrosoft\u002Fcalendars\u002F:id",[],[],[],[],{"operationId":2019,"summary":2020,"method":277,"path":336,"href":2021,"replaces":2022,"renamed":2026,"ignored":2056,"refused":2059,"notes":2072},"events.list","List events","\u002Freference\u002Fevents#events-list",[2023,2024,2025],"GET \u002Fgoogle\u002Fevents","GET \u002Fmicrosoft\u002Fevents","GET \u002Fmicrosoft\u002Fevents\u002FcalendarView",[2027,2028,2030,2032,2033,2036,2038,2040,2043,2046,2049,2050,2053],{"v1":205,"v2":149},{"v1":212,"v2":2029},"from",{"v1":2031,"v2":2029},"startTime",{"v1":209,"v2":2029},{"v1":2034,"v2":2035},"endDate","to",{"v1":2037,"v2":2035},"endTime",{"v1":2039,"v2":2035},"timeMax",{"v1":2041,"v2":2042},"search","q",{"v1":2044,"v2":2045},"maxResults","limit",{"v1":2047,"v2":2048},"singleEvents","expand",{"v1":551,"v2":543},{"v1":2051,"v2":2052},"updatedMin","updatedSince",{"v1":2054,"v2":2055},"eventTypes","eventType",[2057,2058],"timeZone","maxAttendees",[2060,2063,2066,2069],{"name":2061,"reason":2062},"iCalUID","retrieve the event by its ID instead.",{"name":2064,"reason":2065},"$filter","v2 does not pass Outlook query filters through. Use v2's own filters, and read extended properties with include=extendedProperties and extendedPropertyIds.",{"name":2067,"reason":2068},"$expand","read extended properties with include=extendedProperties and extendedPropertyIds, or the provider's whole event with include=raw.",{"name":2070,"reason":2071},"showHiddenInvitations","v2 doesn't return hidden invitations.",[2073,2074,2075,2076,2077,2078],"A `maxResults` above 250 is capped. The remaining events are returned on the next page.","`orderBy=updated` maps to `sort=updated`. `orderBy=startTime` is the order of an expanded window (`expand=true`).","`showDeleted=true` also returns cancelled events. On an incremental sync, deleted events are listed in `meta.deletedIds`.","For `calendarView`, send `from`, `to` and `expand=true`. This returns the window with recurring series expanded.","`privateExtendedProperty` and `sharedExtendedProperty` (`key=value`, repeatable) still search Google events by extended property, to help you move. Search by `metadata.\u003Ckey>` instead.","Outlook's `$filter` and `$expand` are refused. Use v2's own filters, and read extended properties with `include=extendedProperties` and `extendedPropertyIds`.",{"operationId":2080,"summary":2081,"method":287,"path":336,"href":2082,"replaces":2083,"renamed":2086,"ignored":2087,"refused":2088,"notes":2089},"events.create","Create an event","\u002Freference\u002Fevents#events-create",[2084,2085],"POST \u002Fgoogle\u002Fevents","POST \u002Fmicrosoft\u002Fevents",[],[],[],[2090],"The v1 request body (`{ accountId, calendarId, eventData }`) is accepted and converted to the v2 format.",{"operationId":2092,"summary":2093,"method":277,"path":2094,"href":2095,"replaces":2096,"renamed":2099,"ignored":2101,"refused":2102,"notes":2104},"events.get","Get an event","\u002Fv2\u002Fevents\u002F{eventId}","\u002Freference\u002Fevents#events-get",[2097,2098],"GET \u002Fgoogle\u002Fevents\u002F:id","GET \u002Fmicrosoft\u002Fevents\u002F:id",[2100],{"v1":205,"v2":149},[],[2103],{"name":2067,"reason":2068},[],{"operationId":2106,"summary":2107,"method":1855,"path":2094,"href":2108,"replaces":2109,"renamed":2112,"ignored":2113,"refused":2114,"notes":2115},"events.update","Update an event","\u002Freference\u002Fevents#events-update",[2110,2111],"PATCH \u002Fgoogle\u002Fevents\u002F:id","PATCH \u002Fmicrosoft\u002Fevents\u002F:id",[],[],[],[2090],{"operationId":2117,"summary":2118,"method":2119,"path":2094,"href":2120,"replaces":2121,"renamed":2124,"ignored":2129,"refused":2130,"notes":2131},"events.delete","Delete an event","DELETE","\u002Freference\u002Fevents#events-delete",[2122,2123],"DELETE \u002Fgoogle\u002Fevents\u002F:id","DELETE \u002Fmicrosoft\u002Fevents\u002F:id",[2125,2126],{"v1":205,"v2":149},{"v1":2127,"v2":2128},"sendUpdates","notifyAttendees",[],[],[2132],"`sendUpdates=none` maps to `notifyAttendees=false`. `all` and `externalOnly` map to `notifyAttendees=true`.",{"operationId":2134,"summary":2135,"method":277,"path":2136,"href":2137,"replaces":2138,"renamed":2141,"ignored":2147,"refused":2148,"notes":2149},"events.instances","List occurrences of a recurring event","\u002Fv2\u002Fevents\u002F{eventId}\u002Finstances","\u002Freference\u002Fevents#events-instances",[2139,2140],"GET \u002Fgoogle\u002Fevents\u002F:id\u002Finstances","GET \u002Fmicrosoft\u002Fevents\u002F:id\u002Finstances",[2142,2143,2144,2145,2146],{"v1":205,"v2":149},{"v1":212,"v2":2029},{"v1":2034,"v2":2035},{"v1":209,"v2":2029},{"v1":2039,"v2":2035},[],[],[],{"operationId":2151,"summary":2152,"method":277,"path":2153,"href":2154,"replaces":2155,"renamed":2158,"ignored":2164,"refused":2165,"notes":2166},"freeBusy.get","Get free\u002Fbusy for one account","\u002Fv2\u002Ffree-busy","\u002Freference\u002Favailability#freebusy-get",[2156,2157],"GET \u002Fgoogle\u002Ffreebusy","GET \u002Fmicrosoft\u002Ffreebusy",[2159,2162,2163],{"v1":2160,"v2":2161},"calendarIds","calendarRefs",{"v1":209,"v2":2029},{"v1":2039,"v2":2035},[],[],[],{"operationId":2168,"summary":2169,"method":287,"path":2153,"href":2170,"replaces":2171,"renamed":2172,"ignored":2173,"refused":2174,"notes":2175},"freeBusy.query","Get free\u002Fbusy for several accounts","\u002Freference\u002Favailability#freebusy-query",[2156,2157],[],[],[],[2176],"Accepts several accounts in one request. For one account with the v1 query parameter names, use `GET \u002Fv2\u002Ffree-busy`.",{"operationId":2178,"summary":2179,"method":277,"path":1838,"href":2180,"replaces":2181,"renamed":2183,"ignored":2184,"refused":2185,"notes":2186},"meetings.list","List meetings","\u002Freference\u002Fmeetings#meetings-list",[2182],"GET \u002Fzoom\u002Fmeetings",[],[],[],[2187],"v1's `maxResults` is `limit`, `nextPageToken` is `cursor`, and `startDate` and `endDate` are `from` and `to`.",{"operationId":2189,"summary":2190,"method":287,"path":1838,"href":2191,"replaces":2192,"renamed":2194,"ignored":2195,"refused":2196,"notes":2197},"meetings.create","Create a meeting","\u002Freference\u002Fmeetings#meetings-create",[2193],"POST \u002Fzoom\u002Fmeetings",[],[],[],[2198],"v1's `meetingData` fields are top level: `title`, `description`, `startsAt` (was `startDate`), `durationMinutes` (was `duration`), `timezone` (was `timeZone`), `attendees`, `recurrence` (an RRULE) and `settings` (`admission: organizer` for v1's `waitingRoom: true`).",{"operationId":2200,"summary":2201,"method":277,"path":2202,"href":2203,"replaces":2204,"renamed":2206,"ignored":2207,"refused":2208,"notes":2209},"meetings.get","Get a meeting","\u002Fv2\u002Fmeetings\u002F{meetingId}","\u002Freference\u002Fmeetings#meetings-get",[2205],"GET \u002Fzoom\u002Fmeetings\u002F:id",[],[],[],[2210],"v1 returned the meeting in `meeting`; v2 returns it in `data`, like every v2 read.",{"operationId":2212,"summary":2213,"method":1855,"path":2202,"href":2214,"replaces":2215,"renamed":2217,"ignored":2218,"refused":2219,"notes":2220},"meetings.update","Update a meeting","\u002Freference\u002Fmeetings#meetings-update",[2216],"PATCH \u002Fzoom\u002Fmeetings\u002F:id",[],[],[],[2221],"`accountId` is in the query. v1 resent every field with a new passcode; v2 changes only what you send.",{"operationId":2223,"summary":2224,"method":2119,"path":2202,"href":2225,"replaces":2226,"renamed":2228,"ignored":2229,"refused":2230,"notes":2231},"meetings.delete","Delete a meeting","\u002Freference\u002Fmeetings#meetings-delete",[2227],"DELETE \u002Fzoom\u002Fmeetings\u002F:id",[],[],[],[2232],"v1 always sent the cancellation email; in v2 it is `notify=true`.",1794697270591]