[{"data":1,"prerenderedAt":2789},["ShallowReactive",2],{"navigation_docs":3,"-oauth-booking-sync":224,"-oauth-booking-sync-surround":2784},[4,157,184],{"title":5,"path":6,"stem":7,"children":8,"page":156},"Interface","\u002Finterface","1.interface",[9,53,61,68,75,82,90,97,104,111,132,139,145],{"title":10,"path":11,"stem":12,"children":13,"icon":15},"Courts","\u002Finterface\u002Fcourts","1.interface\u002F01.courts\u002F1.index",[14,16],{"title":10,"path":11,"stem":12,"icon":15},"sporty-court-sport",{"title":17,"path":18,"stem":19,"children":20,"icon":22},"Access control and lights","\u002Finterface\u002Fcourts\u002Fintegrations","1.interface\u002F01.courts\u002F5.integrations\u002F1.index",[21,23,28,33],{"title":17,"path":18,"stem":19,"icon":22},"sporty-toggle-large-on",{"title":24,"path":25,"stem":26,"icon":27},"Weblock","\u002Finterface\u002Fcourts\u002Fintegrations\u002Fweblock","1.interface\u002F01.courts\u002F5.integrations\u002F2.weblock","sporty-door-open",{"title":29,"path":30,"stem":31,"icon":32},"Shelly","\u002Finterface\u002Fcourts\u002Fintegrations\u002Fshelly","1.interface\u002F01.courts\u002F5.integrations\u002F3.shelly","sporty-bolt",{"title":34,"path":35,"stem":36,"children":37,"icon":27},"UniFi Access","\u002Finterface\u002Fcourts\u002Fintegrations\u002Funifi-access","1.interface\u002F01.courts\u002F5.integrations\u002F4.unifi-access\u002F1.index",[38,40,44,49],{"title":39,"path":35,"stem":36,"icon":27},"Overview",{"title":41,"path":42,"stem":43,"icon":22},"Connect the VPN","\u002Finterface\u002Fcourts\u002Fintegrations\u002Funifi-access\u002Fvpn","1.interface\u002F01.courts\u002F5.integrations\u002F4.unifi-access\u002F2.vpn",{"title":45,"path":46,"stem":47,"icon":48},"Create an API token","\u002Finterface\u002Fcourts\u002Fintegrations\u002Funifi-access\u002Fapi-token","1.interface\u002F01.courts\u002F5.integrations\u002F4.unifi-access\u002F3.api-token","sporty-gear",{"title":50,"path":51,"stem":52,"icon":15},"Set it up in Club Admin","\u002Finterface\u002Fcourts\u002Fintegrations\u002Funifi-access\u002Fclub-admin","1.interface\u002F01.courts\u002F5.integrations\u002F4.unifi-access\u002F4.club-admin",{"title":54,"path":55,"stem":56,"children":57,"icon":60},"Members","\u002Finterface\u002Fmembers","1.interface\u002F02.members\u002F1.index",[58],{"title":59,"path":55,"stem":56,"icon":60},"Member management","sporty-user-group",{"title":62,"path":63,"stem":64,"children":65,"icon":67},"Memberships","\u002Finterface\u002Fmemberships","1.interface\u002F03.memberships\u002F1.index",[66],{"title":62,"path":63,"stem":64,"icon":67},"sporty-id-card",{"title":69,"path":70,"stem":71,"children":72,"icon":74},"Leagues","\u002Finterface\u002Fleagues","1.interface\u002F04.leagues\u002F1.index",[73],{"title":69,"path":70,"stem":71,"icon":74},"sporty-ranking-star",{"title":76,"path":77,"stem":78,"children":79,"icon":81},"Tournaments","\u002Finterface\u002Ftournaments","1.interface\u002F05.tournaments\u002F1.index",[80],{"title":76,"path":77,"stem":78,"icon":81},"sporty-trophy",{"title":83,"path":84,"stem":85,"children":86,"icon":89},"Payments and subscriptions","\u002Finterface\u002Fpayments","1.interface\u002F06.payments\u002F1.index",[87],{"title":88,"path":84,"stem":85,"icon":89},"Payments","sporty-credit-card",{"title":91,"path":92,"stem":93,"children":94,"icon":96},"Shop","\u002Finterface\u002Fshop","1.interface\u002F07.shop\u002F1.index",[95],{"title":91,"path":92,"stem":93,"icon":96},"sporty-bag-shopping",{"title":98,"path":99,"stem":100,"children":101,"icon":103},"Schools and coaches","\u002Finterface\u002Fschools","1.interface\u002F08.schools\u002F1.index",[102],{"title":98,"path":99,"stem":100,"icon":103},"sporty-graduation-cap",{"title":105,"path":106,"stem":107,"children":108,"icon":110},"Emails","\u002Finterface\u002Femails","1.interface\u002F09.emails\u002F1.index",[109],{"title":105,"path":106,"stem":107,"icon":110},"sporty-envelope",{"title":112,"path":113,"stem":114,"children":115,"icon":117},"Video and cameras","\u002Finterface\u002Fvideo","1.interface\u002F10.video\u002F1.index",[116,118,122,127],{"title":112,"path":113,"stem":114,"icon":117},"sporty-camera-cctv",{"title":119,"path":120,"stem":121,"icon":117},"Dahua cameras","\u002Finterface\u002Fvideo\u002Fdahua","1.interface\u002F10.video\u002F2.dahua",{"title":123,"path":124,"stem":125,"icon":126},"Other RTMP cameras","\u002Finterface\u002Fvideo\u002Frtmp","1.interface\u002F10.video\u002F3.rtmp","sporty-video",{"title":128,"path":129,"stem":130,"icon":131},"Match analytics","\u002Finterface\u002Fvideo\u002Fanalytics","1.interface\u002F10.video\u002F4.analytics","sporty-chart-simple",{"title":133,"path":134,"stem":135,"children":136,"icon":138},"Reports","\u002Finterface\u002Freports","1.interface\u002F11.reports\u002F1.index",[137],{"title":133,"path":134,"stem":135,"icon":138},"sporty-chart-mixed",{"title":140,"path":141,"stem":142,"children":143,"icon":48},"Settings","\u002Finterface\u002Fsettings","1.interface\u002F12.settings\u002F1.index",[144],{"title":140,"path":141,"stem":142,"icon":48},{"title":146,"path":147,"stem":148,"children":149,"icon":151},"Social publishing","\u002Finterface\u002Fsocial","1.interface\u002F13.social\u002F1.index",[150,152],{"title":146,"path":147,"stem":148,"icon":151},"sporty-newspaper",{"title":153,"path":154,"stem":155,"icon":22},"Connecting channels","\u002Finterface\u002Fsocial\u002Fchannels","1.interface\u002F13.social\u002F2.channels",false,{"title":158,"path":159,"stem":160,"children":161,"icon":164},"Website","\u002Fwebsite","2.website\u002F1.index",[162,165,170,174,179],{"title":163,"path":159,"stem":160,"icon":164},"Website builder","sporty-browser",{"title":166,"path":167,"stem":168,"icon":169},"Pages, hero and footer","\u002Fwebsite\u002Fpages","2.website\u002F2.pages","sporty-file-lines",{"title":171,"path":172,"stem":173,"icon":151},"News and events","\u002Fwebsite\u002Fnews-and-events","2.website\u002F3.news-and-events",{"title":175,"path":176,"stem":177,"icon":178},"Coaches and gallery","\u002Fwebsite\u002Fcoaches-and-gallery","2.website\u002F4.coaches-and-gallery","sporty-images",{"title":180,"path":181,"stem":182,"icon":183},"FAQ and sponsors","\u002Fwebsite\u002Ffaq-and-sponsors","2.website\u002F5.faq-and-sponsors","sporty-circle-question",{"title":185,"path":186,"stem":187,"children":188,"icon":190},"OAuth & Sign in with Sporty","\u002Foauth","4.oauth\u002F1.index",[189,191,195,199,203,207,211,215,219],{"title":185,"path":186,"stem":187,"icon":190},"sporty-brackets-curly",{"title":192,"path":193,"stem":194,"icon":190},"Registering an application","\u002Foauth\u002Fclients","4.oauth\u002F2.clients",{"title":196,"path":197,"stem":198,"icon":190},"The authorization code flow","\u002Foauth\u002Fauthorization-code","4.oauth\u002F3.authorization-code",{"title":200,"path":201,"stem":202,"icon":190},"Scopes","\u002Foauth\u002Fscopes","4.oauth\u002F4.scopes",{"title":204,"path":205,"stem":206,"icon":190},"Sign in with Sporty","\u002Foauth\u002Fsign-in-with-sporty","4.oauth\u002F5.sign-in-with-sporty",{"title":208,"path":209,"stem":210,"icon":190},"The partner API","\u002Foauth\u002Fpartner-api","4.oauth\u002F6.partner-api",{"title":212,"path":213,"stem":214,"icon":190},"Syncing bookings","\u002Foauth\u002Fbooking-sync","4.oauth\u002F7.booking-sync",{"title":216,"path":217,"stem":218,"icon":190},"MCP and AI clients","\u002Foauth\u002Fmcp-and-ai-clients","4.oauth\u002F8.mcp-and-ai-clients",{"title":220,"path":221,"stem":222,"icon":223},"Agent checkout","\u002Foauth\u002Fagent-checkout","4.oauth\u002F9.agent-checkout","sporty-cart-shopping",{"id":225,"title":212,"body":226,"description":2775,"extension":2776,"links":2777,"meta":2778,"navigation":2779,"path":213,"seo":2780,"stem":214,"__hash__":2783},"docs\u002F4.oauth\u002F7.booking-sync.md",{"type":227,"value":228,"toc":2755},"minimark",[229,233,250,262,267,270,293,296,300,303,354,363,366,449,456,460,463,493,580,590,594,609,702,795,802,1047,1053,1058,1078,1084,1088,1155,1162,1166,1222,1243,1253,1257,1287,1293,1297,1301,1357,1483,1501,1537,1541,1548,1875,1914,1940,1949,1953,1960,1974,1977,1995,2003,2007,2010,2018,2032,2462,2465,2469,2472,2502,2524,2530,2534,2672,2690,2694,2697,2704,2715,2722,2725,2744,2751],[230,231,232],"p",{},"If you sell a club's courts through your own system, such as a marketplace, a venue manager or\nthe club's website, two diaries have to agree. This page covers both directions:",[234,235,236,244],"ul",{},[237,238,239,243],"li",{},[240,241,242],"strong",{},"You tell us"," about each booking you take, and we hold that court so nobody books it in\nSporty.",[237,245,246,249],{},[240,247,248],{},"We tell you"," about each booking taken, moved or cancelled in Sporty, by signed webhook.",[230,251,252,253,256,257,261],{},"Your integration is a ",[240,254,255],{},"service account",". It holds a\n",[258,259,260],"a",{"href":193},"client credentials"," token and acts for the club, not for any member.",[263,264,266],"h2",{"id":265},"what-a-booking-from-you-is","What a booking from you is",[230,268,269],{},"When you push a booking, the court is held exactly as when a club administrator blocks a slot\nin Sporty:",[234,271,272,283,290],{},[237,273,274,275,278,279,282],{},"It has a ",[240,276,277],{},"name"," (shown in the club's diary) and, optionally, a ",[240,280,281],{},"category",".",[237,284,285,286,289],{},"It has ",[240,287,288],{},"no players and no price",". Sporty takes no payment for it, applies no membership\nlimits and notifies no member about it.",[237,291,292],{},"The court must be free. That is the only rule we check.",[230,294,295],{},"Who played and who paid stays in your system. You are telling the club that the court is\ntaken, not selling the booking through Sporty.",[263,297,299],{"id":298},"before-you-start","Before you start",[230,301,302],{},"The club must grant your application the scopes it needs. For a two-way sync that is:",[304,305,306,319],"table",{},[307,308,309],"thead",{},[310,311,312,316],"tr",{},[313,314,315],"th",{},"Scope",[313,317,318],{},"For",[320,321,322,334,344],"tbody",{},[310,323,324,331],{},[325,326,327],"td",{},[328,329,330],"code",{},"club.courts.read",[325,332,333],{},"Listing the courts, to map them to yours",[310,335,336,341],{},[325,337,338],{},[328,339,340],{},"club.reservations.read",[325,342,343],{},"Reading the diary and its categories, and receiving webhooks",[310,345,346,351],{},[325,347,348],{},[328,349,350],{},"club.reservations.write",[325,352,353],{},"Holding, moving and cancelling bookings",[230,355,356,358,359,362],{},[328,357,350],{}," lets your application cancel ",[240,360,361],{},"any"," booking at the club, including\nmembers' bookings, as the club's staff can. The club grants it knowing that. Use it for\nbookings you must take back, not to tidy the diary.",[230,364,365],{},"Get a token:",[367,368,373],"pre",{"className":369,"code":370,"language":371,"meta":372,"style":372},"language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","curl -X POST https:\u002F\u002Fapi.sporty.plus\u002Foauth2\u002Ftoken \\\n  -d grant_type=client_credentials \\\n  -d client_id=$CLIENT_ID \\\n  -d client_secret=$CLIENT_SECRET \\\n  -d scope=\"club.courts.read club.reservations.read club.reservations.write\"\n","bash","",[328,374,375,398,409,420,431],{"__ignoreMap":372},[376,377,380,384,388,391,394],"span",{"class":378,"line":379},"line",1,[376,381,383],{"class":382},"sBMFI","curl",[376,385,387],{"class":386},"sfazB"," -X",[376,389,390],{"class":386}," POST",[376,392,393],{"class":386}," https:\u002F\u002Fapi.sporty.plus\u002Foauth2\u002Ftoken",[376,395,397],{"class":396},"sTEyZ"," \\\n",[376,399,401,404,407],{"class":378,"line":400},2,[376,402,403],{"class":386},"  -d",[376,405,406],{"class":386}," grant_type=client_credentials",[376,408,397],{"class":396},[376,410,412,414,417],{"class":378,"line":411},3,[376,413,403],{"class":386},[376,415,416],{"class":386}," client_id=",[376,418,419],{"class":396},"$CLIENT_ID \\\n",[376,421,423,425,428],{"class":378,"line":422},4,[376,424,403],{"class":386},[376,426,427],{"class":386}," client_secret=",[376,429,430],{"class":396},"$CLIENT_SECRET \\\n",[376,432,434,436,439,443,446],{"class":378,"line":433},5,[376,435,403],{"class":386},[376,437,438],{"class":386}," scope=",[376,440,442],{"class":441},"sMK4o","\"",[376,444,445],{"class":386},"club.courts.read club.reservations.read club.reservations.write",[376,447,448],{"class":441},"\"\n",[230,450,451,452,455],{},"If your application acts for several clubs, send ",[328,453,454],{},"X-Club: \u003Cclub id>"," on every request below.\nWebhooks are set per club.",[263,457,459],{"id":458},"categories","Categories",[230,461,462],{},"A booking may carry one of the club's reservation categories, the labels its administrators\ngive held slots (\"League\", \"Tennis school\"). The club's diary shows it in the category's colour.",[367,464,466],{"className":369,"code":465,"language":371,"meta":372,"style":372},"curl https:\u002F\u002Fapi.sporty.plus\u002Fapi\u002Fpartner\u002Fv1\u002Freservation-categories \\\n  -H \"Authorization: Bearer $ACCESS_TOKEN\"\n",[328,467,468,477],{"__ignoreMap":372},[376,469,470,472,475],{"class":378,"line":379},[376,471,383],{"class":382},[376,473,474],{"class":386}," https:\u002F\u002Fapi.sporty.plus\u002Fapi\u002Fpartner\u002Fv1\u002Freservation-categories",[376,476,397],{"class":396},[376,478,479,482,485,488,491],{"class":378,"line":400},[376,480,481],{"class":386},"  -H",[376,483,484],{"class":441}," \"",[376,486,487],{"class":386},"Authorization: Bearer ",[376,489,490],{"class":396},"$ACCESS_TOKEN",[376,492,448],{"class":441},[367,494,498],{"className":495,"code":496,"language":497,"meta":372,"style":372},"language-json shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","{ \"data\": [ { \"id\": 3, \"name\": \"Liga\", \"color\": \"#ea47bc\" } ] }\n","json",[328,499,500],{"__ignoreMap":372},[376,501,502,505,507,511,513,516,519,522,524,527,529,531,535,538,540,542,544,546,548,551,553,555,557,560,562,564,566,569,571,574,577],{"class":378,"line":379},[376,503,504],{"class":441},"{",[376,506,484],{"class":441},[376,508,510],{"class":509},"spNyl","data",[376,512,442],{"class":441},[376,514,515],{"class":441},":",[376,517,518],{"class":441}," [",[376,520,521],{"class":441}," {",[376,523,484],{"class":441},[376,525,526],{"class":382},"id",[376,528,442],{"class":441},[376,530,515],{"class":441},[376,532,534],{"class":533},"sbssI"," 3",[376,536,537],{"class":441},",",[376,539,484],{"class":441},[376,541,277],{"class":382},[376,543,442],{"class":441},[376,545,515],{"class":441},[376,547,484],{"class":441},[376,549,550],{"class":386},"Liga",[376,552,442],{"class":441},[376,554,537],{"class":441},[376,556,484],{"class":441},[376,558,559],{"class":382},"color",[376,561,442],{"class":441},[376,563,515],{"class":441},[376,565,484],{"class":441},[376,567,568],{"class":386},"#ea47bc",[376,570,442],{"class":441},[376,572,573],{"class":441}," }",[376,575,576],{"class":441}," ]",[376,578,579],{"class":441}," }\n",[230,581,582,583,585,586,589],{},"This needs ",[328,584,340],{},". A club may have none; ",[328,587,588],{},"category_id"," is optional.",[263,591,593],{"id":592},"holding-a-court","Holding a court",[230,595,596,597,600,601,605,606,282],{},"To find a free slot first, ",[328,598,599],{},"GET \u002Favailability?date=…"," lists each court's slots, free or taken;\nsee ",[258,602,604],{"href":603},"\u002Foauth\u002Fpartner-api#get-availability","the partner API",". Holding a court checks it again,\nso a slot taken in the meantime still gets ",[328,607,608],{},"409 slot_taken",[367,610,612],{"className":369,"code":611,"language":371,"meta":372,"style":372},"curl -X POST https:\u002F\u002Fapi.sporty.plus\u002Fapi\u002Fpartner\u002Fv1\u002Freservations \\\n  -H \"Authorization: Bearer $ACCESS_TOKEN\" \\\n  -H \"Content-Type: application\u002Fjson\" \\\n  -d '{\n    \"external_id\": \"bk_8f3a21\",\n    \"court_id\": 412,\n    \"from\": \"2026-10-06T17:00:00+02:00\",\n    \"to\": \"2026-10-06T18:30:00+02:00\",\n    \"name\": \"Online: Horvat\"\n  }'\n",[328,613,614,627,641,654,664,669,675,681,687,693],{"__ignoreMap":372},[376,615,616,618,620,622,625],{"class":378,"line":379},[376,617,383],{"class":382},[376,619,387],{"class":386},[376,621,390],{"class":386},[376,623,624],{"class":386}," https:\u002F\u002Fapi.sporty.plus\u002Fapi\u002Fpartner\u002Fv1\u002Freservations",[376,626,397],{"class":396},[376,628,629,631,633,635,637,639],{"class":378,"line":400},[376,630,481],{"class":386},[376,632,484],{"class":441},[376,634,487],{"class":386},[376,636,490],{"class":396},[376,638,442],{"class":441},[376,640,397],{"class":396},[376,642,643,645,647,650,652],{"class":378,"line":411},[376,644,481],{"class":386},[376,646,484],{"class":441},[376,648,649],{"class":386},"Content-Type: application\u002Fjson",[376,651,442],{"class":441},[376,653,397],{"class":396},[376,655,656,658,661],{"class":378,"line":422},[376,657,403],{"class":386},[376,659,660],{"class":441}," '",[376,662,663],{"class":386},"{\n",[376,665,666],{"class":378,"line":433},[376,667,668],{"class":386},"    \"external_id\": \"bk_8f3a21\",\n",[376,670,672],{"class":378,"line":671},6,[376,673,674],{"class":386},"    \"court_id\": 412,\n",[376,676,678],{"class":378,"line":677},7,[376,679,680],{"class":386},"    \"from\": \"2026-10-06T17:00:00+02:00\",\n",[376,682,684],{"class":378,"line":683},8,[376,685,686],{"class":386},"    \"to\": \"2026-10-06T18:30:00+02:00\",\n",[376,688,690],{"class":378,"line":689},9,[376,691,692],{"class":386},"    \"name\": \"Online: Horvat\"\n",[376,694,696,699],{"class":378,"line":695},10,[376,697,698],{"class":386},"  }",[376,700,701],{"class":441},"'\n",[304,703,704,715],{},[307,705,706],{},[310,707,708,711,713],{},[313,709,710],{},"Field",[313,712],{},[313,714],{},[320,716,717,733,748,769,781],{},[310,718,719,724,727],{},[325,720,721],{},[328,722,723],{},"external_id",[325,725,726],{},"required",[325,728,729,732],{},[240,730,731],{},"Your"," id for the booking, up to 191 characters.",[310,734,735,740,742],{},[325,736,737],{},[328,738,739],{},"court_id",[325,741,726],{},[325,743,744,745,282],{},"A court from ",[328,746,747],{},"GET \u002Fcourts",[310,749,750,759,761],{},[325,751,752,755,756],{},[328,753,754],{},"from",", ",[328,757,758],{},"to",[325,760,726],{},[325,762,763,764,766,767,282],{},"ISO 8601 with an offset. ",[328,765,758],{}," must be after ",[328,768,754],{},[310,770,771,775,778],{},[325,772,773],{},[328,774,277],{},[325,776,777],{},"optional",[325,779,780],{},"What the club sees in its diary, up to 255 characters.",[310,782,783,787,789],{},[325,784,785],{},[328,786,588],{},[325,788,777],{},[325,790,791,792,282],{},"One of the club's reservation categories, from ",[328,793,794],{},"GET \u002Freservation-categories",[230,796,797,798,801],{},"The answer is ",[328,799,800],{},"201"," with the booking:",[367,803,805],{"className":495,"code":804,"language":497,"meta":372,"style":372},"{\n  \"data\": {\n    \"id\": 918273,\n    \"court_id\": 412,\n    \"court_name\": \"Teren 3\",\n    \"from\": \"2026-10-06T15:00:00+00:00\",\n    \"to\": \"2026-10-06T16:30:00+00:00\",\n    \"category_id\": null,\n    \"approved\": false,\n    \"cancelled\": false,\n    \"source\": \"partner\",\n    \"external_id\": \"bk_8f3a21\",\n    \"name\": \"Online: Horvat\",\n    \"updated_at\": \"2026-09-29T11:20:04+00:00\"\n  }\n}\n",[328,806,807,811,825,842,857,877,896,915,928,942,955,976,996,1016,1035,1041],{"__ignoreMap":372},[376,808,809],{"class":378,"line":379},[376,810,663],{"class":441},[376,812,813,816,818,820,822],{"class":378,"line":400},[376,814,815],{"class":441},"  \"",[376,817,510],{"class":509},[376,819,442],{"class":441},[376,821,515],{"class":441},[376,823,824],{"class":441}," {\n",[376,826,827,830,832,834,836,839],{"class":378,"line":411},[376,828,829],{"class":441},"    \"",[376,831,526],{"class":382},[376,833,442],{"class":441},[376,835,515],{"class":441},[376,837,838],{"class":533}," 918273",[376,840,841],{"class":441},",\n",[376,843,844,846,848,850,852,855],{"class":378,"line":422},[376,845,829],{"class":441},[376,847,739],{"class":382},[376,849,442],{"class":441},[376,851,515],{"class":441},[376,853,854],{"class":533}," 412",[376,856,841],{"class":441},[376,858,859,861,864,866,868,870,873,875],{"class":378,"line":433},[376,860,829],{"class":441},[376,862,863],{"class":382},"court_name",[376,865,442],{"class":441},[376,867,515],{"class":441},[376,869,484],{"class":441},[376,871,872],{"class":386},"Teren 3",[376,874,442],{"class":441},[376,876,841],{"class":441},[376,878,879,881,883,885,887,889,892,894],{"class":378,"line":671},[376,880,829],{"class":441},[376,882,754],{"class":382},[376,884,442],{"class":441},[376,886,515],{"class":441},[376,888,484],{"class":441},[376,890,891],{"class":386},"2026-10-06T15:00:00+00:00",[376,893,442],{"class":441},[376,895,841],{"class":441},[376,897,898,900,902,904,906,908,911,913],{"class":378,"line":677},[376,899,829],{"class":441},[376,901,758],{"class":382},[376,903,442],{"class":441},[376,905,515],{"class":441},[376,907,484],{"class":441},[376,909,910],{"class":386},"2026-10-06T16:30:00+00:00",[376,912,442],{"class":441},[376,914,841],{"class":441},[376,916,917,919,921,923,925],{"class":378,"line":683},[376,918,829],{"class":441},[376,920,588],{"class":382},[376,922,442],{"class":441},[376,924,515],{"class":441},[376,926,927],{"class":441}," null,\n",[376,929,930,932,935,937,939],{"class":378,"line":689},[376,931,829],{"class":441},[376,933,934],{"class":382},"approved",[376,936,442],{"class":441},[376,938,515],{"class":441},[376,940,941],{"class":441}," false,\n",[376,943,944,946,949,951,953],{"class":378,"line":695},[376,945,829],{"class":441},[376,947,948],{"class":382},"cancelled",[376,950,442],{"class":441},[376,952,515],{"class":441},[376,954,941],{"class":441},[376,956,958,960,963,965,967,969,972,974],{"class":378,"line":957},11,[376,959,829],{"class":441},[376,961,962],{"class":382},"source",[376,964,442],{"class":441},[376,966,515],{"class":441},[376,968,484],{"class":441},[376,970,971],{"class":386},"partner",[376,973,442],{"class":441},[376,975,841],{"class":441},[376,977,979,981,983,985,987,989,992,994],{"class":378,"line":978},12,[376,980,829],{"class":441},[376,982,723],{"class":382},[376,984,442],{"class":441},[376,986,515],{"class":441},[376,988,484],{"class":441},[376,990,991],{"class":386},"bk_8f3a21",[376,993,442],{"class":441},[376,995,841],{"class":441},[376,997,999,1001,1003,1005,1007,1009,1012,1014],{"class":378,"line":998},13,[376,1000,829],{"class":441},[376,1002,277],{"class":382},[376,1004,442],{"class":441},[376,1006,515],{"class":441},[376,1008,484],{"class":441},[376,1010,1011],{"class":386},"Online: Horvat",[376,1013,442],{"class":441},[376,1015,841],{"class":441},[376,1017,1019,1021,1024,1026,1028,1030,1033],{"class":378,"line":1018},14,[376,1020,829],{"class":441},[376,1022,1023],{"class":382},"updated_at",[376,1025,442],{"class":441},[376,1027,515],{"class":441},[376,1029,484],{"class":441},[376,1031,1032],{"class":386},"2026-09-29T11:20:04+00:00",[376,1034,448],{"class":441},[376,1036,1038],{"class":378,"line":1037},15,[376,1039,1040],{"class":441},"  }\n",[376,1042,1044],{"class":378,"line":1043},16,[376,1045,1046],{"class":441},"}\n",[230,1048,1049,1050,1052],{},"Keep ",[328,1051,526],{},". It is how you move or cancel the booking. Times always come back in UTC.",[1054,1055,1057],"h3",{"id":1056},"sending-it-twice-is-safe","Sending it twice is safe",[230,1059,1060,1062,1063,1065,1066,1069,1070,1073,1074,1077],{},[328,1061,723],{}," makes the request idempotent. Sending the same ",[328,1064,723],{}," again while that\nbooking stands answers ",[328,1067,1068],{},"200"," with the existing booking, ",[240,1071,1072],{},"unchanged",". A retry after a timeout\ntherefore never holds a second court. A re-send with different times does not move the booking;\nuse ",[328,1075,1076],{},"PATCH"," to move it.",[230,1079,1080,1081,1083],{},"If the booking has since been cancelled, by you or by the club, sending its ",[328,1082,723],{}," again\nbooks it afresh.",[1054,1085,1087],{"id":1086},"when-the-court-is-taken","When the court is taken",[367,1089,1091],{"className":495,"code":1090,"language":497,"meta":372,"style":372},"{\n  \"error\": \"slot_taken\",\n  \"error_description\": \"The court is already booked for some of that time.\",\n  \"conflicting_reservation_id\": 918102\n}\n",[328,1092,1093,1097,1117,1137,1151],{"__ignoreMap":372},[376,1094,1095],{"class":378,"line":379},[376,1096,663],{"class":441},[376,1098,1099,1101,1104,1106,1108,1110,1113,1115],{"class":378,"line":400},[376,1100,815],{"class":441},[376,1102,1103],{"class":509},"error",[376,1105,442],{"class":441},[376,1107,515],{"class":441},[376,1109,484],{"class":441},[376,1111,1112],{"class":386},"slot_taken",[376,1114,442],{"class":441},[376,1116,841],{"class":441},[376,1118,1119,1121,1124,1126,1128,1130,1133,1135],{"class":378,"line":411},[376,1120,815],{"class":441},[376,1122,1123],{"class":509},"error_description",[376,1125,442],{"class":441},[376,1127,515],{"class":441},[376,1129,484],{"class":441},[376,1131,1132],{"class":386},"The court is already booked for some of that time.",[376,1134,442],{"class":441},[376,1136,841],{"class":441},[376,1138,1139,1141,1144,1146,1148],{"class":378,"line":422},[376,1140,815],{"class":441},[376,1142,1143],{"class":509},"conflicting_reservation_id",[376,1145,442],{"class":441},[376,1147,515],{"class":441},[376,1149,1150],{"class":533}," 918102\n",[376,1152,1153],{"class":378,"line":433},[376,1154,1046],{"class":441},[230,1156,1157,1158,1161],{},"Status ",[328,1159,1160],{},"409",". A booking may start exactly when another ends; any overlap longer than zero is a\nclash.",[263,1163,1165],{"id":1164},"moving-a-booking","Moving a booking",[367,1167,1169],{"className":369,"code":1168,"language":371,"meta":372,"style":372},"curl -X PATCH https:\u002F\u002Fapi.sporty.plus\u002Fapi\u002Fpartner\u002Fv1\u002Freservations\u002F918273 \\\n  -H \"Authorization: Bearer $ACCESS_TOKEN\" \\\n  -H \"Content-Type: application\u002Fjson\" \\\n  -d '{\"court_id\": 413, \"from\": \"2026-10-06T18:00:00+02:00\", \"to\": \"2026-10-06T19:30:00+02:00\"}'\n",[328,1170,1171,1185,1199,1211],{"__ignoreMap":372},[376,1172,1173,1175,1177,1180,1183],{"class":378,"line":379},[376,1174,383],{"class":382},[376,1176,387],{"class":386},[376,1178,1179],{"class":386}," PATCH",[376,1181,1182],{"class":386}," https:\u002F\u002Fapi.sporty.plus\u002Fapi\u002Fpartner\u002Fv1\u002Freservations\u002F918273",[376,1184,397],{"class":396},[376,1186,1187,1189,1191,1193,1195,1197],{"class":378,"line":400},[376,1188,481],{"class":386},[376,1190,484],{"class":441},[376,1192,487],{"class":386},[376,1194,490],{"class":396},[376,1196,442],{"class":441},[376,1198,397],{"class":396},[376,1200,1201,1203,1205,1207,1209],{"class":378,"line":411},[376,1202,481],{"class":386},[376,1204,484],{"class":441},[376,1206,649],{"class":386},[376,1208,442],{"class":441},[376,1210,397],{"class":396},[376,1212,1213,1215,1217,1220],{"class":378,"line":422},[376,1214,403],{"class":386},[376,1216,660],{"class":441},[376,1218,1219],{"class":386},"{\"court_id\": 413, \"from\": \"2026-10-06T18:00:00+02:00\", \"to\": \"2026-10-06T19:30:00+02:00\"}",[376,1221,701],{"class":441},[230,1223,1224,1225,755,1227,755,1229,755,1231,755,1233,1235,1236,1239,1240,282],{},"Send only what changes: ",[328,1226,739],{},[328,1228,277],{},[328,1230,588],{},[328,1232,754],{},[328,1234,758],{},". Either end of the time\nmay change on its own, so ",[328,1237,1238],{},"{\"to\": \"…\"}"," extends or shortens a booking. The new slot is checked\nagainst every other booking, and the answer is the updated booking. A change that would end the\nbooking before it starts is refused with ",[328,1241,1242],{},"422 invalid_times",[230,1244,1245,1246,1249,1250,282],{},"You can only move bookings ",[240,1247,1248],{},"your application pushed",". Bookings made in Sporty belong to the\nmembers and the club. For those, and for bookings that have been cancelled, the answer is ",[328,1251,1252],{},"404",[263,1254,1256],{"id":1255},"cancelling-a-booking","Cancelling a booking",[367,1258,1260],{"className":369,"code":1259,"language":371,"meta":372,"style":372},"curl -X DELETE https:\u002F\u002Fapi.sporty.plus\u002Fapi\u002Fpartner\u002Fv1\u002Freservations\u002F918273 \\\n  -H \"Authorization: Bearer $ACCESS_TOKEN\"\n",[328,1261,1262,1275],{"__ignoreMap":372},[376,1263,1264,1266,1268,1271,1273],{"class":378,"line":379},[376,1265,383],{"class":382},[376,1267,387],{"class":386},[376,1269,1270],{"class":386}," DELETE",[376,1272,1182],{"class":386},[376,1274,397],{"class":396},[376,1276,1277,1279,1281,1283,1285],{"class":378,"line":400},[376,1278,481],{"class":386},[376,1280,484],{"class":441},[376,1282,487],{"class":386},[376,1284,490],{"class":396},[376,1286,448],{"class":441},[230,1288,797,1289,1292],{},[328,1290,1291],{},"204",". This works for any booking at the club, and it runs as a club\nadministrator's cancellation would. If the booking was a member's, they are refunded to their\nclub wallet and notified that it was cancelled.",[263,1294,1296],{"id":1295},"hearing-about-bookings-made-in-sporty","Hearing about bookings made in Sporty",[1054,1298,1300],{"id":1299},"setting-the-webhook","Setting the webhook",[367,1302,1304],{"className":369,"code":1303,"language":371,"meta":372,"style":372},"curl -X PUT https:\u002F\u002Fapi.sporty.plus\u002Fapi\u002Fpartner\u002Fv1\u002Fwebhook \\\n  -H \"Authorization: Bearer $ACCESS_TOKEN\" \\\n  -H \"Content-Type: application\u002Fjson\" \\\n  -d '{\"url\": \"https:\u002F\u002Fbookings.example.com\u002Fhooks\u002Fsporty\"}'\n",[328,1305,1306,1320,1334,1346],{"__ignoreMap":372},[376,1307,1308,1310,1312,1315,1318],{"class":378,"line":379},[376,1309,383],{"class":382},[376,1311,387],{"class":386},[376,1313,1314],{"class":386}," PUT",[376,1316,1317],{"class":386}," https:\u002F\u002Fapi.sporty.plus\u002Fapi\u002Fpartner\u002Fv1\u002Fwebhook",[376,1319,397],{"class":396},[376,1321,1322,1324,1326,1328,1330,1332],{"class":378,"line":400},[376,1323,481],{"class":386},[376,1325,484],{"class":441},[376,1327,487],{"class":386},[376,1329,490],{"class":396},[376,1331,442],{"class":441},[376,1333,397],{"class":396},[376,1335,1336,1338,1340,1342,1344],{"class":378,"line":411},[376,1337,481],{"class":386},[376,1339,484],{"class":441},[376,1341,649],{"class":386},[376,1343,442],{"class":441},[376,1345,397],{"class":396},[376,1347,1348,1350,1352,1355],{"class":378,"line":422},[376,1349,403],{"class":386},[376,1351,660],{"class":441},[376,1353,1354],{"class":386},"{\"url\": \"https:\u002F\u002Fbookings.example.com\u002Fhooks\u002Fsporty\"}",[376,1356,701],{"class":441},[367,1358,1360],{"className":495,"code":1359,"language":497,"meta":372,"style":372},"{\n  \"data\": {\n    \"url\": \"https:\u002F\u002Fbookings.example.com\u002Fhooks\u002Fsporty\",\n    \"delivered_up_to\": \"2026-09-29T11:25:00+00:00\",\n    \"last_delivered_at\": null,\n    \"last_failed_at\": null,\n    \"last_error\": null,\n    \"secret\": \"whsec_…\"\n  }\n}\n",[328,1361,1362,1366,1378,1398,1418,1431,1444,1457,1475,1479],{"__ignoreMap":372},[376,1363,1364],{"class":378,"line":379},[376,1365,663],{"class":441},[376,1367,1368,1370,1372,1374,1376],{"class":378,"line":400},[376,1369,815],{"class":441},[376,1371,510],{"class":509},[376,1373,442],{"class":441},[376,1375,515],{"class":441},[376,1377,824],{"class":441},[376,1379,1380,1382,1385,1387,1389,1391,1394,1396],{"class":378,"line":411},[376,1381,829],{"class":441},[376,1383,1384],{"class":382},"url",[376,1386,442],{"class":441},[376,1388,515],{"class":441},[376,1390,484],{"class":441},[376,1392,1393],{"class":386},"https:\u002F\u002Fbookings.example.com\u002Fhooks\u002Fsporty",[376,1395,442],{"class":441},[376,1397,841],{"class":441},[376,1399,1400,1402,1405,1407,1409,1411,1414,1416],{"class":378,"line":422},[376,1401,829],{"class":441},[376,1403,1404],{"class":382},"delivered_up_to",[376,1406,442],{"class":441},[376,1408,515],{"class":441},[376,1410,484],{"class":441},[376,1412,1413],{"class":386},"2026-09-29T11:25:00+00:00",[376,1415,442],{"class":441},[376,1417,841],{"class":441},[376,1419,1420,1422,1425,1427,1429],{"class":378,"line":433},[376,1421,829],{"class":441},[376,1423,1424],{"class":382},"last_delivered_at",[376,1426,442],{"class":441},[376,1428,515],{"class":441},[376,1430,927],{"class":441},[376,1432,1433,1435,1438,1440,1442],{"class":378,"line":671},[376,1434,829],{"class":441},[376,1436,1437],{"class":382},"last_failed_at",[376,1439,442],{"class":441},[376,1441,515],{"class":441},[376,1443,927],{"class":441},[376,1445,1446,1448,1451,1453,1455],{"class":378,"line":677},[376,1447,829],{"class":441},[376,1449,1450],{"class":382},"last_error",[376,1452,442],{"class":441},[376,1454,515],{"class":441},[376,1456,927],{"class":441},[376,1458,1459,1461,1464,1466,1468,1470,1473],{"class":378,"line":683},[376,1460,829],{"class":441},[376,1462,1463],{"class":382},"secret",[376,1465,442],{"class":441},[376,1467,515],{"class":441},[376,1469,484],{"class":441},[376,1471,1472],{"class":386},"whsec_…",[376,1474,448],{"class":441},[376,1476,1477],{"class":378,"line":689},[376,1478,1040],{"class":441},[376,1480,1481],{"class":378,"line":695},[376,1482,1046],{"class":441},[1484,1485,1487,1492,1493,1496,1497,1500],"callout",{"icon":1486},"i-heroicons-key",[240,1488,1489,1491],{},[328,1490,1463],{}," is shown once",", in this response. Store it. To replace it, send\n",[328,1494,1495],{},"\"rotate_secret\": true"," with the next ",[328,1498,1499],{},"PUT",", and the response carries the new one.",[234,1502,1503,1517,1520,1527],{},[237,1504,1505,1506,1508,1509,1512,1513,1516],{},"Sending ",[328,1507,1499],{}," again changes the URL. ",[328,1510,1511],{},"GET \u002Fwebhook"," shows the current settings and how\ndelivery is going; ",[328,1514,1515],{},"DELETE \u002Fwebhook"," stops delivery.",[237,1518,1519],{},"There is one webhook per application and club.",[237,1521,1522,1523,282],{},"A new webhook starts from the moment you set it. For anything earlier, see\n",[258,1524,1526],{"href":1525},"#catching-up","catching up",[237,1528,1529,1530,1533,1534,282],{},"The URL must be ",[328,1531,1532],{},"https",", carry no credentials, and resolve to a public address. Anything\nelse is refused with ",[328,1535,1536],{},"422",[1054,1538,1540],{"id":1539},"what-arrives","What arrives",[230,1542,1543,1544,1547],{},"About once a minute, while anything has changed, you get a ",[328,1545,1546],{},"POST"," like this one:",[367,1549,1551],{"className":495,"code":1550,"language":497,"meta":372,"style":372},"{\n  \"id\": \"5d1c4e0a-4a59-4b8e-9a0e-2a7b8f6c9d12\",\n  \"club_id\": 57,\n  \"sent_at\": \"2026-09-29T11:31:07+00:00\",\n  \"events\": [\n    {\n      \"type\": \"reservation.created\",\n      \"reservation\": {\n        \"id\": 918301,\n        \"court_id\": 412,\n        \"court_name\": \"Teren 3\",\n        \"from\": \"2026-10-07T16:00:00+00:00\",\n        \"to\": \"2026-10-07T17:00:00+00:00\",\n        \"category_id\": null,\n        \"approved\": true,\n        \"cancelled\": false,\n        \"source\": \"sporty\",\n        \"external_id\": null,\n        \"name\": null,\n        \"updated_at\": \"2026-09-29T11:30:58+00:00\"\n      }\n    }\n  ]\n}\n",[328,1552,1553,1557,1576,1592,1612,1626,1631,1652,1665,1681,1695,1713,1732,1751,1763,1776,1788,1808,1821,1834,1852,1858,1864,1870],{"__ignoreMap":372},[376,1554,1555],{"class":378,"line":379},[376,1556,663],{"class":441},[376,1558,1559,1561,1563,1565,1567,1569,1572,1574],{"class":378,"line":400},[376,1560,815],{"class":441},[376,1562,526],{"class":509},[376,1564,442],{"class":441},[376,1566,515],{"class":441},[376,1568,484],{"class":441},[376,1570,1571],{"class":386},"5d1c4e0a-4a59-4b8e-9a0e-2a7b8f6c9d12",[376,1573,442],{"class":441},[376,1575,841],{"class":441},[376,1577,1578,1580,1583,1585,1587,1590],{"class":378,"line":411},[376,1579,815],{"class":441},[376,1581,1582],{"class":509},"club_id",[376,1584,442],{"class":441},[376,1586,515],{"class":441},[376,1588,1589],{"class":533}," 57",[376,1591,841],{"class":441},[376,1593,1594,1596,1599,1601,1603,1605,1608,1610],{"class":378,"line":422},[376,1595,815],{"class":441},[376,1597,1598],{"class":509},"sent_at",[376,1600,442],{"class":441},[376,1602,515],{"class":441},[376,1604,484],{"class":441},[376,1606,1607],{"class":386},"2026-09-29T11:31:07+00:00",[376,1609,442],{"class":441},[376,1611,841],{"class":441},[376,1613,1614,1616,1619,1621,1623],{"class":378,"line":433},[376,1615,815],{"class":441},[376,1617,1618],{"class":509},"events",[376,1620,442],{"class":441},[376,1622,515],{"class":441},[376,1624,1625],{"class":441}," [\n",[376,1627,1628],{"class":378,"line":671},[376,1629,1630],{"class":441},"    {\n",[376,1632,1633,1636,1639,1641,1643,1645,1648,1650],{"class":378,"line":677},[376,1634,1635],{"class":441},"      \"",[376,1637,1638],{"class":382},"type",[376,1640,442],{"class":441},[376,1642,515],{"class":441},[376,1644,484],{"class":441},[376,1646,1647],{"class":386},"reservation.created",[376,1649,442],{"class":441},[376,1651,841],{"class":441},[376,1653,1654,1656,1659,1661,1663],{"class":378,"line":683},[376,1655,1635],{"class":441},[376,1657,1658],{"class":382},"reservation",[376,1660,442],{"class":441},[376,1662,515],{"class":441},[376,1664,824],{"class":441},[376,1666,1667,1670,1672,1674,1676,1679],{"class":378,"line":689},[376,1668,1669],{"class":441},"        \"",[376,1671,526],{"class":533},[376,1673,442],{"class":441},[376,1675,515],{"class":441},[376,1677,1678],{"class":533}," 918301",[376,1680,841],{"class":441},[376,1682,1683,1685,1687,1689,1691,1693],{"class":378,"line":695},[376,1684,1669],{"class":441},[376,1686,739],{"class":533},[376,1688,442],{"class":441},[376,1690,515],{"class":441},[376,1692,854],{"class":533},[376,1694,841],{"class":441},[376,1696,1697,1699,1701,1703,1705,1707,1709,1711],{"class":378,"line":957},[376,1698,1669],{"class":441},[376,1700,863],{"class":533},[376,1702,442],{"class":441},[376,1704,515],{"class":441},[376,1706,484],{"class":441},[376,1708,872],{"class":386},[376,1710,442],{"class":441},[376,1712,841],{"class":441},[376,1714,1715,1717,1719,1721,1723,1725,1728,1730],{"class":378,"line":978},[376,1716,1669],{"class":441},[376,1718,754],{"class":533},[376,1720,442],{"class":441},[376,1722,515],{"class":441},[376,1724,484],{"class":441},[376,1726,1727],{"class":386},"2026-10-07T16:00:00+00:00",[376,1729,442],{"class":441},[376,1731,841],{"class":441},[376,1733,1734,1736,1738,1740,1742,1744,1747,1749],{"class":378,"line":998},[376,1735,1669],{"class":441},[376,1737,758],{"class":533},[376,1739,442],{"class":441},[376,1741,515],{"class":441},[376,1743,484],{"class":441},[376,1745,1746],{"class":386},"2026-10-07T17:00:00+00:00",[376,1748,442],{"class":441},[376,1750,841],{"class":441},[376,1752,1753,1755,1757,1759,1761],{"class":378,"line":1018},[376,1754,1669],{"class":441},[376,1756,588],{"class":533},[376,1758,442],{"class":441},[376,1760,515],{"class":441},[376,1762,927],{"class":441},[376,1764,1765,1767,1769,1771,1773],{"class":378,"line":1037},[376,1766,1669],{"class":441},[376,1768,934],{"class":533},[376,1770,442],{"class":441},[376,1772,515],{"class":441},[376,1774,1775],{"class":441}," true,\n",[376,1777,1778,1780,1782,1784,1786],{"class":378,"line":1043},[376,1779,1669],{"class":441},[376,1781,948],{"class":533},[376,1783,442],{"class":441},[376,1785,515],{"class":441},[376,1787,941],{"class":441},[376,1789,1791,1793,1795,1797,1799,1801,1804,1806],{"class":378,"line":1790},17,[376,1792,1669],{"class":441},[376,1794,962],{"class":533},[376,1796,442],{"class":441},[376,1798,515],{"class":441},[376,1800,484],{"class":441},[376,1802,1803],{"class":386},"sporty",[376,1805,442],{"class":441},[376,1807,841],{"class":441},[376,1809,1811,1813,1815,1817,1819],{"class":378,"line":1810},18,[376,1812,1669],{"class":441},[376,1814,723],{"class":533},[376,1816,442],{"class":441},[376,1818,515],{"class":441},[376,1820,927],{"class":441},[376,1822,1824,1826,1828,1830,1832],{"class":378,"line":1823},19,[376,1825,1669],{"class":441},[376,1827,277],{"class":533},[376,1829,442],{"class":441},[376,1831,515],{"class":441},[376,1833,927],{"class":441},[376,1835,1837,1839,1841,1843,1845,1847,1850],{"class":378,"line":1836},20,[376,1838,1669],{"class":441},[376,1840,1023],{"class":533},[376,1842,442],{"class":441},[376,1844,515],{"class":441},[376,1846,484],{"class":441},[376,1848,1849],{"class":386},"2026-09-29T11:30:58+00:00",[376,1851,448],{"class":441},[376,1853,1855],{"class":378,"line":1854},21,[376,1856,1857],{"class":441},"      }\n",[376,1859,1861],{"class":378,"line":1860},22,[376,1862,1863],{"class":441},"    }\n",[376,1865,1867],{"class":378,"line":1866},23,[376,1868,1869],{"class":441},"  ]\n",[376,1871,1873],{"class":378,"line":1872},24,[376,1874,1046],{"class":441},[234,1876,1877,1899,1904],{},[237,1878,1879,1881,1882],{},[328,1880,1638],{}," is one of the following:\n",[234,1883,1884,1888,1894],{},[237,1885,1886],{},[328,1887,1647],{},[237,1889,1890,1893],{},[328,1891,1892],{},"reservation.updated",", when the booking was moved, renamed or otherwise changed;",[237,1895,1896,282],{},[328,1897,1898],{},"reservation.cancelled",[237,1900,1901,1903],{},[328,1902,1618],{}," holds up to about 100 changes, oldest first.",[237,1905,1906,1907,1909,1910,1913],{},"Members' bookings arrive without names (",[328,1908,277],{}," is ",[328,1911,1912],{},"null",") and without the people in them.\nYou see that the court is taken, and when.",[230,1915,1916,1922,1923,1925,1926,1928,1929,1931,1932,1934,1935,1937,1938,282],{},[240,1917,1918,1919,1921],{},"Do not rely on ",[328,1920,1638],{}," to be complete."," Changes are collected between deliveries, so a\nbooking made and cancelled within the same minute arrives only as ",[328,1924,1898],{},".\nTreat ",[328,1927,1658],{}," as the booking's current state and apply that: if ",[328,1930,948],{}," is true,\nfree the court; otherwise, hold it on ",[328,1933,739],{}," from ",[328,1936,754],{}," to ",[328,1939,758],{},[230,1941,1942,1945,1946,1948],{},[240,1943,1944],{},"You do not hear about your own changes."," A booking you push or move is not sent back to you.\nWhen you cancel a member's booking, it does come back as ",[328,1947,1898],{},". That event is\nharmless, since the court is already free in your copy.",[1054,1950,1952],{"id":1951},"answering","Answering",[230,1954,1955,1956,1959],{},"Answer with any ",[328,1957,1958],{},"2xx"," within 10 seconds. Anything else counts as a failure: another status, a\ntimeout, or a redirect (we do not follow redirects). The same changes, plus anything newer, are\nthen sent again about a minute later, and so on until you accept them. Nothing is dropped, and\nthe order is kept.",[230,1961,1962,1963,1966,1967,1970,1971,1973],{},"This means ",[240,1964,1965],{},"you will sometimes receive a change twice",". Make handling idempotent: key on\n",[328,1968,1969],{},"reservation.id",", and ignore a copy whose ",[328,1972,1023],{}," is not newer than what you already have.",[230,1975,1976],{},"Delivery stops in any of these cases:",[234,1978,1979,1982,1985,1988],{},[237,1980,1981],{},"the club withdraws your grant;",[237,1983,1984],{},"your application is revoked;",[237,1986,1987],{},"you delete the webhook;",[237,1989,1990,1991,282],{},"the club ",[258,1992,1994],{"href":1993},"#who-the-club-sees","stops the webhook",[230,1996,1997,1999,2000,2002],{},[328,1998,1511],{}," shows ",[328,2001,1450],{}," if deliveries are failing.",[1054,2004,2006],{"id":2005},"verifying-the-signature","Verifying the signature",[230,2008,2009],{},"Every delivery carries:",[367,2011,2016],{"className":2012,"code":2014,"language":2015},[2013],"language-text","Sporty-Signature: t=1790680267,v1=6c4b1e9f…\n","text",[328,2017,2014],{"__ignoreMap":372},[230,2019,2020,2023,2024,2027,2028,2031],{},[328,2021,2022],{},"v1"," is the hex HMAC-SHA256 of ",[328,2025,2026],{},"{t}.{raw body}",", keyed with your webhook secret. Check it\nagainst the ",[240,2029,2030],{},"raw"," request body before parsing it, and reject old timestamps so a captured\ndelivery cannot be replayed.",[2033,2034,2035,2417],"code-group",{},[367,2036,2041],{"className":2037,"code":2038,"filename":2039,"language":2040,"meta":372,"style":372},"language-js shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","import crypto from 'node:crypto'\n\nexport function verifySporty(rawBody, header, secret, toleranceSeconds = 300) {\n  const parts = Object.fromEntries(header.split(',').map((kv) => kv.split('=')))\n  const expected = crypto.createHmac('sha256', secret).update(`${parts.t}.${rawBody}`).digest('hex')\n  const fresh = Math.abs(Date.now() \u002F 1000 - Number(parts.t)) \u003C= toleranceSeconds\n\n  return fresh && parts.v1?.length === expected.length\n    && crypto.timingSafeEqual(Buffer.from(parts.v1), Buffer.from(expected))\n}\n","Node.js","js",[328,2042,2043,2061,2067,2112,2188,2270,2329,2333,2365,2413],{"__ignoreMap":372},[376,2044,2045,2049,2052,2054,2056,2059],{"class":378,"line":379},[376,2046,2048],{"class":2047},"s7zQu","import",[376,2050,2051],{"class":396}," crypto ",[376,2053,754],{"class":2047},[376,2055,660],{"class":441},[376,2057,2058],{"class":386},"node:crypto",[376,2060,701],{"class":441},[376,2062,2063],{"class":378,"line":400},[376,2064,2066],{"emptyLinePlaceholder":2065},true,"\n",[376,2068,2069,2072,2075,2079,2082,2086,2088,2091,2093,2096,2098,2101,2104,2107,2110],{"class":378,"line":411},[376,2070,2071],{"class":2047},"export",[376,2073,2074],{"class":509}," function",[376,2076,2078],{"class":2077},"s2Zo4"," verifySporty",[376,2080,2081],{"class":441},"(",[376,2083,2085],{"class":2084},"sHdIc","rawBody",[376,2087,537],{"class":441},[376,2089,2090],{"class":2084}," header",[376,2092,537],{"class":441},[376,2094,2095],{"class":2084}," secret",[376,2097,537],{"class":441},[376,2099,2100],{"class":2084}," toleranceSeconds",[376,2102,2103],{"class":441}," =",[376,2105,2106],{"class":533}," 300",[376,2108,2109],{"class":441},")",[376,2111,824],{"class":441},[376,2113,2114,2117,2120,2122,2125,2127,2130,2133,2136,2138,2141,2143,2146,2148,2150,2152,2154,2157,2159,2161,2164,2166,2169,2172,2174,2176,2178,2180,2183,2185],{"class":378,"line":422},[376,2115,2116],{"class":509},"  const",[376,2118,2119],{"class":396}," parts",[376,2121,2103],{"class":441},[376,2123,2124],{"class":396}," Object",[376,2126,282],{"class":441},[376,2128,2129],{"class":2077},"fromEntries",[376,2131,2081],{"class":2132},"swJcz",[376,2134,2135],{"class":396},"header",[376,2137,282],{"class":441},[376,2139,2140],{"class":2077},"split",[376,2142,2081],{"class":2132},[376,2144,2145],{"class":441},"'",[376,2147,537],{"class":386},[376,2149,2145],{"class":441},[376,2151,2109],{"class":2132},[376,2153,282],{"class":441},[376,2155,2156],{"class":2077},"map",[376,2158,2081],{"class":2132},[376,2160,2081],{"class":441},[376,2162,2163],{"class":2084},"kv",[376,2165,2109],{"class":441},[376,2167,2168],{"class":509}," =>",[376,2170,2171],{"class":396}," kv",[376,2173,282],{"class":441},[376,2175,2140],{"class":2077},[376,2177,2081],{"class":2132},[376,2179,2145],{"class":441},[376,2181,2182],{"class":386},"=",[376,2184,2145],{"class":441},[376,2186,2187],{"class":2132},")))\n",[376,2189,2190,2192,2195,2197,2200,2202,2205,2207,2209,2212,2214,2216,2218,2220,2222,2225,2227,2230,2233,2235,2238,2241,2243,2246,2248,2251,2253,2255,2258,2260,2262,2265,2267],{"class":378,"line":433},[376,2191,2116],{"class":509},[376,2193,2194],{"class":396}," expected",[376,2196,2103],{"class":441},[376,2198,2199],{"class":396}," crypto",[376,2201,282],{"class":441},[376,2203,2204],{"class":2077},"createHmac",[376,2206,2081],{"class":2132},[376,2208,2145],{"class":441},[376,2210,2211],{"class":386},"sha256",[376,2213,2145],{"class":441},[376,2215,537],{"class":441},[376,2217,2095],{"class":396},[376,2219,2109],{"class":2132},[376,2221,282],{"class":441},[376,2223,2224],{"class":2077},"update",[376,2226,2081],{"class":2132},[376,2228,2229],{"class":441},"`${",[376,2231,2232],{"class":396},"parts",[376,2234,282],{"class":441},[376,2236,2237],{"class":396},"t",[376,2239,2240],{"class":441},"}",[376,2242,282],{"class":386},[376,2244,2245],{"class":441},"${",[376,2247,2085],{"class":396},[376,2249,2250],{"class":441},"}`",[376,2252,2109],{"class":2132},[376,2254,282],{"class":441},[376,2256,2257],{"class":2077},"digest",[376,2259,2081],{"class":2132},[376,2261,2145],{"class":441},[376,2263,2264],{"class":386},"hex",[376,2266,2145],{"class":441},[376,2268,2269],{"class":2132},")\n",[376,2271,2272,2274,2277,2279,2282,2284,2287,2289,2292,2294,2297,2300,2303,2306,2309,2312,2314,2316,2318,2320,2323,2326],{"class":378,"line":671},[376,2273,2116],{"class":509},[376,2275,2276],{"class":396}," fresh",[376,2278,2103],{"class":441},[376,2280,2281],{"class":396}," Math",[376,2283,282],{"class":441},[376,2285,2286],{"class":2077},"abs",[376,2288,2081],{"class":2132},[376,2290,2291],{"class":396},"Date",[376,2293,282],{"class":441},[376,2295,2296],{"class":2077},"now",[376,2298,2299],{"class":2132},"() ",[376,2301,2302],{"class":441},"\u002F",[376,2304,2305],{"class":533}," 1000",[376,2307,2308],{"class":441}," -",[376,2310,2311],{"class":2077}," Number",[376,2313,2081],{"class":2132},[376,2315,2232],{"class":396},[376,2317,282],{"class":441},[376,2319,2237],{"class":396},[376,2321,2322],{"class":2132},")) ",[376,2324,2325],{"class":441},"\u003C=",[376,2327,2328],{"class":396}," toleranceSeconds\n",[376,2330,2331],{"class":378,"line":677},[376,2332,2066],{"emptyLinePlaceholder":2065},[376,2334,2335,2338,2340,2343,2345,2347,2349,2352,2355,2358,2360,2362],{"class":378,"line":683},[376,2336,2337],{"class":2047},"  return",[376,2339,2276],{"class":396},[376,2341,2342],{"class":441}," &&",[376,2344,2119],{"class":396},[376,2346,282],{"class":441},[376,2348,2022],{"class":396},[376,2350,2351],{"class":441},"?.",[376,2353,2354],{"class":396},"length",[376,2356,2357],{"class":441}," ===",[376,2359,2194],{"class":396},[376,2361,282],{"class":441},[376,2363,2364],{"class":396},"length\n",[376,2366,2367,2370,2372,2374,2377,2379,2382,2384,2386,2388,2390,2392,2394,2396,2398,2401,2403,2405,2407,2410],{"class":378,"line":689},[376,2368,2369],{"class":441},"    &&",[376,2371,2199],{"class":396},[376,2373,282],{"class":441},[376,2375,2376],{"class":2077},"timingSafeEqual",[376,2378,2081],{"class":2132},[376,2380,2381],{"class":396},"Buffer",[376,2383,282],{"class":441},[376,2385,754],{"class":2077},[376,2387,2081],{"class":2132},[376,2389,2232],{"class":396},[376,2391,282],{"class":441},[376,2393,2022],{"class":396},[376,2395,2109],{"class":2132},[376,2397,537],{"class":441},[376,2399,2400],{"class":396}," Buffer",[376,2402,282],{"class":441},[376,2404,754],{"class":2077},[376,2406,2081],{"class":2132},[376,2408,2409],{"class":396},"expected",[376,2411,2412],{"class":2132},"))\n",[376,2414,2415],{"class":378,"line":695},[376,2416,1046],{"class":441},[367,2418,2423],{"className":2419,"code":2420,"filename":2421,"language":2422,"meta":372,"style":372},"language-php shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","function verifySporty(string $rawBody, string $header, string $secret, int $tolerance = 300): bool\n{\n    parse_str(str_replace(',', '&', $header), $parts);\n    $expected = hash_hmac('sha256', $parts['t'].'.'.$rawBody, $secret);\n\n    return abs(time() - (int) $parts['t']) \u003C= $tolerance\n        && hash_equals($expected, $parts['v1'] ?? '');\n}\n","PHP","php",[328,2424,2425,2430,2434,2439,2444,2448,2453,2458],{"__ignoreMap":372},[376,2426,2427],{"class":378,"line":379},[376,2428,2429],{},"function verifySporty(string $rawBody, string $header, string $secret, int $tolerance = 300): bool\n",[376,2431,2432],{"class":378,"line":400},[376,2433,663],{},[376,2435,2436],{"class":378,"line":411},[376,2437,2438],{},"    parse_str(str_replace(',', '&', $header), $parts);\n",[376,2440,2441],{"class":378,"line":422},[376,2442,2443],{},"    $expected = hash_hmac('sha256', $parts['t'].'.'.$rawBody, $secret);\n",[376,2445,2446],{"class":378,"line":433},[376,2447,2066],{"emptyLinePlaceholder":2065},[376,2449,2450],{"class":378,"line":671},[376,2451,2452],{},"    return abs(time() - (int) $parts['t']) \u003C= $tolerance\n",[376,2454,2455],{"class":378,"line":677},[376,2456,2457],{},"        && hash_equals($expected, $parts['v1'] ?? '');\n",[376,2459,2460],{"class":378,"line":683},[376,2461,1046],{},[230,2463,2464],{},"Allow for a delivery arriving several minutes late: it is resent until you accept it, but it is\nsigned afresh each time, so the tolerance only has to cover clock skew.",[263,2466,2468],{"id":2467},"catching-up","Catching up",[230,2470,2471],{},"The same changes are available on request. Use this after an outage on your side, when you\nfirst connect, or as a nightly check that the two diaries agree:",[367,2473,2475],{"className":369,"code":2474,"language":371,"meta":372,"style":372},"curl \"https:\u002F\u002Fapi.sporty.plus\u002Fapi\u002Fpartner\u002Fv1\u002Freservations?updated_since=2026-09-28T00:00:00Z\" \\\n  -H \"Authorization: Bearer $ACCESS_TOKEN\"\n",[328,2476,2477,2490],{"__ignoreMap":372},[376,2478,2479,2481,2483,2486,2488],{"class":378,"line":379},[376,2480,383],{"class":382},[376,2482,484],{"class":441},[376,2484,2485],{"class":386},"https:\u002F\u002Fapi.sporty.plus\u002Fapi\u002Fpartner\u002Fv1\u002Freservations?updated_since=2026-09-28T00:00:00Z",[376,2487,442],{"class":441},[376,2489,397],{"class":396},[376,2491,2492,2494,2496,2498,2500],{"class":378,"line":400},[376,2493,481],{"class":386},[376,2495,484],{"class":441},[376,2497,487],{"class":386},[376,2499,490],{"class":396},[376,2501,448],{"class":441},[230,2503,2504,2505,2508,2509,2512,2513,2516,2517,2520,2521,2523],{},"With ",[328,2506,2507],{},"updated_since",", the list holds every booking changed at or after that instant, oldest\nchange first, ",[240,2510,2511],{},"cancellations included",". Page through with ",[328,2514,2515],{},"per_page"," (up to 100) and ",[328,2518,2519],{},"page",".\nTo continue later, start from the last ",[328,2522,1023],{}," you saw; you may see that booking again,\nwhich is harmless.",[230,2525,2526,2529],{},[328,2527,2528],{},"GET \u002Freservations\u002F{id}"," reads a single booking, cancelled or not.",[263,2531,2533],{"id":2532},"errors","Errors",[304,2535,2536,2550],{},[307,2537,2538],{},[310,2539,2540,2543,2547],{},[313,2541,2542],{},"Status",[313,2544,2545],{},[328,2546,1103],{},[313,2548,2549],{},"Meaning",[320,2551,2552,2568,2582,2596,2610,2627,2644,2658],{},[310,2553,2554,2558,2562],{},[325,2555,2556],{},[328,2557,1160],{},[325,2559,2560],{},[328,2561,1112],{},[325,2563,2564,2565,2567],{},"The court is booked for some of that time. ",[328,2566,1143],{}," says by what.",[310,2569,2570,2574,2579],{},[325,2571,2572],{},[328,2573,1536],{},[325,2575,2576],{},[328,2577,2578],{},"unknown_court",[325,2580,2581],{},"The club has no such court.",[310,2583,2584,2588,2593],{},[325,2585,2586],{},[328,2587,1536],{},[325,2589,2590],{},[328,2591,2592],{},"unknown_category",[325,2594,2595],{},"The club has no such reservation category.",[310,2597,2598,2602,2607],{},[325,2599,2600],{},[328,2601,1536],{},[325,2603,2604],{},[328,2605,2606],{},"invalid_times",[325,2608,2609],{},"The booking would end before it starts.",[310,2611,2612,2616,2621],{},[325,2613,2614],{},[328,2615,1252],{},[325,2617,2618],{},[328,2619,2620],{},"not_found",[325,2622,2623,2624,2626],{},"No such booking at this club; for ",[328,2625,1076],{},", none your application pushed.",[310,2628,2629,2633,2638],{},[325,2630,2631],{},[328,2632,1160],{},[325,2634,2635],{},[328,2636,2637],{},"not_cancelable",[325,2639,2640,2641,2643],{},"The booking could not be cancelled; ",[328,2642,1123],{}," says why.",[310,2645,2646,2650,2655],{},[325,2647,2648],{},[328,2649,1160],{},[325,2651,2652],{},[328,2653,2654],{},"no_acting_admin",[325,2656,2657],{},"No club administrator is on record as having granted your application. Ask the club to grant it again.",[310,2659,2660,2664,2669],{},[325,2661,2662],{},[328,2663,1160],{},[325,2665,2666],{},[328,2667,2668],{},"no_courts",[325,2670,2671],{},"The club has no courts to book.",[230,2673,2674,2675,755,2678,755,2681,2684,2685,2689],{},"The general errors (",[328,2676,2677],{},"401",[328,2679,2680],{},"403 insufficient_scope",[328,2682,2683],{},"429"," and so on) are listed on the\n",[258,2686,2688],{"href":2687},"\u002Foauth\u002Fpartner-api#errors","partner API"," page.",[263,2691,2693],{"id":2692},"who-the-club-sees","Who the club sees",[230,2695,2696],{},"Every booking you hold, move or cancel is recorded against the club administrator who granted\nyour application access. If the club registered the application itself, it is recorded against\nthe administrator who registered it. When you cancel a member's booking, that administrator is\nthe name in the member's notification. Tell the club, so nobody is surprised.",[230,2698,2699,2700,2703],{},"The club also sees your webhook. Under ",[240,2701,2702],{},"Settings → API clients",", a club administrator sees:",[234,2705,2706,2709,2712],{},[237,2707,2708],{},"its URL;",[237,2710,2711],{},"whether it is delivering, failing, or has sent nothing yet;",[237,2713,2714],{},"when it last delivered, and the last error while it is failing.",[230,2716,2717,2718,2721],{},"The secret is never shown there. ",[240,2719,2720],{},"Keep your endpoint answering."," A webhook that fails for days\nis what a club notices, and what it stops.",[230,2723,2724],{},"The administrator can stop your webhook without withdrawing your access. After that:",[234,2726,2727,2735,2738],{},[237,2728,2729,2731,2732,2734],{},[328,2730,1511],{}," answers ",[328,2733,1252],{},";",[237,2736,2737],{},"your token still works;",[237,2739,2740,2741,2743],{},"a ",[328,2742,1499],{}," sets a new webhook, with a new secret, which hears about changes from that moment on.",[230,2745,2746,2747,2750],{},"Call ",[328,2748,2749],{},"GET \u002Freservations?updated_since=…"," from when your last delivery arrived, to pick up what\nhappened while the webhook was stopped.",[2752,2753,2754],"style",{},"html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}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 .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .sHdIc, html code.shiki .sHdIc{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#EEFFFF;--shiki-default-font-style:italic;--shiki-dark:#BABED8;--shiki-dark-font-style:italic}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}",{"title":372,"searchDepth":400,"depth":400,"links":2756},[2757,2758,2759,2760,2764,2765,2766,2772,2773,2774],{"id":265,"depth":400,"text":266},{"id":298,"depth":400,"text":299},{"id":458,"depth":400,"text":459},{"id":592,"depth":400,"text":593,"children":2761},[2762,2763],{"id":1056,"depth":411,"text":1057},{"id":1086,"depth":411,"text":1087},{"id":1164,"depth":400,"text":1165},{"id":1255,"depth":400,"text":1256},{"id":1295,"depth":400,"text":1296,"children":2767},[2768,2769,2770,2771],{"id":1299,"depth":411,"text":1300},{"id":1539,"depth":411,"text":1540},{"id":1951,"depth":411,"text":1952},{"id":2005,"depth":411,"text":2006},{"id":2467,"depth":400,"text":2468},{"id":2532,"depth":400,"text":2533},{"id":2692,"depth":400,"text":2693},"Keep your booking system and a club's Sporty diary in agreement, in both directions.","md",null,{},{"icon":190},{"title":2781,"description":2782},"Two-way court booking sync with the Sporty partner API","Hold courts for bookings taken in your system, and receive signed webhooks for bookings taken in Sporty.","WIf6k5HCN7tzfbx2XNbjtVE_XqGLWkDjF6f2iOdz-Z8",[2785,2787],{"title":208,"path":209,"stem":210,"description":2786,"icon":190,"children":-1},"What an access token can actually read and write, and what it deliberately cannot.",{"title":216,"path":217,"stem":218,"description":2788,"icon":190,"children":-1},"Dynamic client registration, and what a self-registered client can and cannot do.",1790691909306]