[{"data":1,"prerenderedAt":6509},["ShallowReactive",2],{"api-nav":3,"api-guide:\u002Fapi\u002Fclients\u002Fbuild-your-own":351,"api-spec:guide:\u002Fapi\u002Fclients\u002Fbuild-your-own":6475},[4,28,57,95,115,301,317,331],{"id":5,"title":6,"links":7,"groups":27},"start","Start here",[8,11,15,18,21,24],{"title":9,"to":10},"Overview","\u002Fapi",{"title":12,"to":13,"status":14},"Quickstart","\u002Fapi\u002Fquickstart","available",{"title":16,"to":17,"status":14},"Environments","\u002Fapi\u002Fenvironments",{"title":19,"to":20,"status":14},"Authentication","\u002Fapi\u002Fauthentication",{"title":22,"to":23,"status":14},"Organizations","\u002Fapi\u002Forganizations",{"title":25,"to":26,"status":14},"Permissions","\u002Fapi\u002Fpermissions",[],{"id":29,"title":30,"links":31,"groups":56},"concepts","Concepts",[32,35,38,41,44,47,50,53],{"title":33,"to":34,"status":14},"Requests and responses","\u002Fapi\u002Frequests-and-responses",{"title":36,"to":37,"status":14},"Identifiers","\u002Fapi\u002Fidentifiers",{"title":39,"to":40,"status":14},"Pagination","\u002Fapi\u002Fpagination",{"title":42,"to":43},"Errors","\u002Fapi\u002Ferrors",{"title":45,"to":46,"status":14},"Rate limits","\u002Fapi\u002Frate-limits",{"title":48,"to":49,"status":14},"Retries","\u002Fapi\u002Fretries-and-idempotency",{"title":51,"to":52,"status":14},"Security","\u002Fapi\u002Fsecurity",{"title":54,"to":55,"status":14},"Versioning","\u002Fapi\u002Fversioning",[],{"id":58,"title":59,"links":60,"groups":94},"resources","Guides",[61,64,67,70,73,76,79,82,85,88,91],{"title":62,"to":63,"status":14},"Students","\u002Fapi\u002Fguides\u002Fstudents",{"title":65,"to":66,"status":14},"Bulk registration","\u002Fapi\u002Fguides\u002Fbulk-registration",{"title":68,"to":69,"status":14},"Passwords","\u002Fapi\u002Fguides\u002Fpasswords",{"title":71,"to":72,"status":14},"Supervisors","\u002Fapi\u002Fguides\u002Fsupervisors",{"title":74,"to":75,"status":14},"Reference data","\u002Fapi\u002Fguides\u002Freference-data",{"title":77,"to":78,"status":14},"API account","\u002Fapi\u002Fguides\u002Fapi-account",{"title":80,"to":81,"status":14},"Sign-in links","\u002Fapi\u002Fguides\u002Fsign-in-links",{"title":83,"to":84,"status":14},"Exams","\u002Fapi\u002Fguides\u002Fexams",{"title":86,"to":87,"status":14},"Applications","\u002Fapi\u002Fguides\u002Fapplications",{"title":89,"to":90,"status":14},"Group challenges","\u002Fapi\u002Fguides\u002Fgroup-challenges",{"title":92,"to":93,"status":14},"Certificates and reports","\u002Fapi\u002Fguides\u002Fcertificates-and-reports",[],{"id":96,"title":97,"links":98,"groups":114},"tutorials","Tutorials",[99,102,105,108,111],{"title":100,"to":101,"status":14},"Register and apply","\u002Fapi\u002Ftutorials\u002Fregister-and-apply",{"title":103,"to":104,"status":14},"Send a student to the panel","\u002Fapi\u002Ftutorials\u002Fsend-student-to-panel",{"title":106,"to":107,"status":14},"Change an application","\u002Fapi\u002Ftutorials\u002Fchange-an-application",{"title":109,"to":110,"status":14},"Collect results","\u002Fapi\u002Ftutorials\u002Fcollect-results",{"title":112,"to":113,"status":14},"Token handling","\u002Fapi\u002Ftutorials\u002Ftoken-handling",[],{"id":116,"title":117,"links":118,"groups":125},"reference","Reference",[119,122],{"title":120,"to":121},"All endpoints","\u002Fapi\u002Freference",{"title":123,"to":124},"Sandbox console","\u002Fapi\u002Fconsole",[126,135,145,166,206,212,230,255,271],{"tag":127,"slug":128,"links":129},"Health","health",[130],{"title":131,"to":132,"method":133,"deprecated":134},"Check that the API is up","\u002Fapi\u002Freference\u002Fget-health","GET",false,{"tag":77,"slug":136,"links":137},"api-account",[138,142],{"title":139,"to":140,"method":141,"deprecated":134},"Revoke the token you send, before it expires","\u002Fapi\u002Freference\u002Frevoke-token","POST",{"title":143,"to":144,"method":133,"deprecated":134},"Fetch the API account your token belongs to","\u002Fapi\u002Freference\u002Fget-current-api-account",{"tag":74,"slug":146,"links":147},"reference-data",[148,151,154,157,160,163],{"title":149,"to":150,"method":133,"deprecated":134},"List the countries a student can be registered in","\u002Fapi\u002Freference\u002Flist-countries",{"title":152,"to":153,"method":133,"deprecated":134},"Fetch one country by its id","\u002Fapi\u002Freference\u002Fget-country",{"title":155,"to":156,"method":133,"deprecated":134},"List the grades a student can be registered with","\u002Fapi\u002Freference\u002Flist-grades",{"title":158,"to":159,"method":133,"deprecated":134},"Fetch one grade by its id","\u002Fapi\u002Freference\u002Fget-grade",{"title":161,"to":162,"method":133,"deprecated":134},"List the organizations and their ids","\u002Fapi\u002Freference\u002Flist-organizations",{"title":164,"to":165,"method":133,"deprecated":134},"Fetch one organization by its id","\u002Fapi\u002Freference\u002Fget-organization",{"tag":62,"slug":167,"links":168},"students",[169,172,175,178,181,184,187,191,194,197,200,203],{"title":170,"to":171,"method":133,"deprecated":134},"List your students","\u002Fapi\u002Freference\u002Flist-students",{"title":173,"to":174,"method":141,"deprecated":134},"Register a student","\u002Fapi\u002Freference\u002Fregister-student",{"title":176,"to":177,"method":141,"deprecated":134},"Check a registration without registering the student","\u002Fapi\u002Freference\u002Fcheck-student-registration",{"title":179,"to":180,"method":141,"deprecated":134},"Register many students at once","\u002Fapi\u002Freference\u002Fcreate-student-import",{"title":182,"to":183,"method":133,"deprecated":134},"Follow a batch of students you sent","\u002Fapi\u002Freference\u002Fget-student-import",{"title":185,"to":186,"method":133,"deprecated":134},"Fetch one of your students","\u002Fapi\u002Freference\u002Fget-student",{"title":188,"to":189,"method":190,"deprecated":134},"Update one of your students","\u002Fapi\u002Freference\u002Fupdate-student","PUT",{"title":192,"to":193,"method":190,"deprecated":134},"Set the sign-in password of one of your students","\u002Fapi\u002Freference\u002Fset-student-password",{"title":195,"to":196,"method":133,"deprecated":134},"List your students who can use this organization","\u002Fapi\u002Freference\u002Flist-org-students",{"title":198,"to":199,"method":133,"deprecated":134},"Fetch one of your students, if they can use this organization","\u002Fapi\u002Freference\u002Fget-org-student",{"title":201,"to":202,"method":190,"deprecated":134},"Update one of your students and give them access to this organization","\u002Fapi\u002Freference\u002Fupdate-org-student",{"title":204,"to":205,"method":190,"deprecated":134},"Link one of your students to a supervisor on this organization","\u002Fapi\u002Freference\u002Flink-student-supervisor",{"tag":80,"slug":207,"links":208},"sign-in-links",[209],{"title":210,"to":211,"method":141,"deprecated":134},"Create a single-use sign-in link for one of your students","\u002Fapi\u002Freference\u002Fcreate-signin-link",{"tag":83,"slug":213,"links":214},"exams",[215,218,221,224,227],{"title":216,"to":217,"method":133,"deprecated":134},"List the exams open for applications","\u002Fapi\u002Freference\u002Flist-exams",{"title":219,"to":220,"method":133,"deprecated":134},"List an organization’s exam categories","\u002Fapi\u002Freference\u002Flist-exam-categories",{"title":222,"to":223,"method":133,"deprecated":134},"Fetch one exam category","\u002Fapi\u002Freference\u002Fget-exam-category",{"title":225,"to":226,"method":133,"deprecated":134},"List the exams one of your students can apply to","\u002Fapi\u002Freference\u002Flist-available-exams",{"title":228,"to":229,"method":133,"deprecated":134},"Fetch one exam that is open for applications","\u002Fapi\u002Freference\u002Fget-exam",{"tag":86,"slug":231,"links":232},"applications",[233,236,239,242,245,248,251],{"title":234,"to":235,"method":133,"deprecated":134},"List your students’ applications in this organization","\u002Fapi\u002Freference\u002Flist-applications",{"title":237,"to":238,"method":141,"deprecated":134},"Enter one of your students for an exam","\u002Fapi\u002Freference\u002Fcreate-application",{"title":240,"to":241,"method":133,"deprecated":134},"List your students’ applications for one exam","\u002Fapi\u002Freference\u002Flist-exam-applications",{"title":243,"to":244,"method":133,"deprecated":134},"List one of your students’ applications in this organization","\u002Fapi\u002Freference\u002Flist-student-applications",{"title":246,"to":247,"method":133,"deprecated":134},"Fetch one of your students’ applications","\u002Fapi\u002Freference\u002Fget-application",{"title":249,"to":250,"method":190,"deprecated":134},"Move one of your students’ applications to another exam","\u002Fapi\u002Freference\u002Fmove-application",{"title":252,"to":253,"method":254,"deprecated":134},"Withdraw one of your students from an exam","\u002Fapi\u002Freference\u002Fdelete-application","DELETE",{"tag":256,"slug":257,"links":258},"Documents","documents",[259,262,265,268],{"title":260,"to":261,"method":133,"deprecated":134},"Download a certificate file","\u002Fapi\u002Freference\u002Fdownload-certificate",{"title":263,"to":264,"method":133,"deprecated":134},"List one of your students’ released certificates","\u002Fapi\u002Freference\u002Flist-student-certificates",{"title":266,"to":267,"method":133,"deprecated":134},"Download a result report file","\u002Fapi\u002Freference\u002Fdownload-report",{"title":269,"to":270,"method":133,"deprecated":134},"List one of your students’ released result reports","\u002Fapi\u002Freference\u002Flist-student-reports",{"tag":89,"slug":272,"links":273},"group-challenges",[274,277,280,283,286,289,292,295,298],{"title":275,"to":276,"method":133,"deprecated":134},"List the group challenges an organization runs","\u002Fapi\u002Freference\u002Flist-group-challenges",{"title":278,"to":279,"method":133,"deprecated":134},"Fetch one group challenge","\u002Fapi\u002Freference\u002Fget-group-challenge",{"title":281,"to":282,"method":133,"deprecated":134},"List the groups your students are in for a group challenge","\u002Fapi\u002Freference\u002Flist-group-challenge-groups",{"title":284,"to":285,"method":133,"deprecated":134},"Fetch one group, with its steps and files","\u002Fapi\u002Freference\u002Fget-group-challenge-group",{"title":287,"to":288,"method":133,"deprecated":134},"List what has happened in one group","\u002Fapi\u002Freference\u002Flist-group-challenge-activity",{"title":290,"to":291,"method":141,"deprecated":134},"Send a group’s finished work for one of your students","\u002Fapi\u002Freference\u002Fsubmit-group-challenge-work",{"title":293,"to":294,"method":141,"deprecated":134},"Submit one step of a group for one of your students","\u002Fapi\u002Freference\u002Fsubmit-group-challenge-step",{"title":296,"to":297,"method":133,"deprecated":134},"List your students’ eligibility and groups for a group challenge","\u002Fapi\u002Freference\u002Flist-group-challenge-students",{"title":299,"to":300,"method":133,"deprecated":134},"Fetch one of your students’ eligibility and group for a group challenge","\u002Fapi\u002Freference\u002Fget-group-challenge-student",{"id":302,"title":303,"links":304,"groups":316},"clients","Clients",[305,307,310,313],{"title":9,"to":306,"status":14},"\u002Fapi\u002Fclients",{"title":308,"to":309,"status":14},"Node.js","\u002Fapi\u002Fclients\u002Fnode",{"title":311,"to":312,"status":14},"PHP","\u002Fapi\u002Fclients\u002Fphp",{"title":314,"to":315,"status":14},"Build your own","\u002Fapi\u002Fclients\u002Fbuild-your-own",[],{"id":318,"title":319,"links":320,"groups":330},"agents","AI agents",[321,324,327],{"title":322,"to":323},"AI connections","\u002Fapi\u002Fmcp",{"title":325,"to":326},"What it can do","\u002Fapi\u002Fmcp\u002Ftools",{"title":328,"to":329},"Agent skills","\u002Fapi\u002Fskills",[],{"id":332,"title":333,"links":334,"groups":350},"help","Help",[335,338,341,344,347],{"title":336,"to":337,"status":14},"Glossary","\u002Fapi\u002Fglossary",{"title":339,"to":340,"status":14},"FAQ","\u002Fapi\u002Ffaq",{"title":342,"to":343,"status":14},"Troubleshooting","\u002Fapi\u002Ftroubleshooting",{"title":345,"to":346,"status":14},"Support","\u002Fapi\u002Fsupport",{"title":348,"to":349},"Changelog","\u002Fapi\u002Fchangelog",[],{"id":352,"title":353,"body":354,"description":6459,"extension":6460,"meta":6461,"navTitle":314,"navigation":1450,"operations":6462,"order":6471,"path":315,"section":302,"seo":6472,"status":14,"stem":6473,"__hash__":6474},"apiGuides\u002Fapi\u002Fclients\u002Fbuild-your-own.md","Build your own client",{"type":355,"value":356,"toc":6432},"minimark",[357,361,391,403,408,413,420,566,586,595,618,646,650,653,787,793,867,879,894,907,911,929,933,1013,1017,1042,1046,1055,1110,1114,1122,1126,1133,1176,1201,1205,1312,1334,1338,1344,1348,1354,1373,3104,3107,3516,3520,3527,3543,5450,5452,5818,5825,5829,5832,5912,5916,5919,5922,6013,6017,6086,6090,6174,6176,6210,6214,6253,6257,6314,6318,6354,6358,6364,6414,6419,6428],[358,359,360],"p",{},"The official clients cover Node.js and PHP. The API has no special access for them, so a client\nin any language works as well as theirs, provided it follows the rules on this page. You'll find:",[362,363,364,368,388],"ol",{},[365,366,367],"li",{},"The rules every client has to follow, with the reason for each.",[365,369,370,371,375,376,379,380,383,384,387],{},"A minimal client in Node.js (",[372,373,374],"code",{},"fetch"," and ",[372,377,378],{},"jsonwebtoken",", about 90 lines) and in PHP (",[372,381,382],{},"curl","\nand ",[372,385,386],{},"firebase\u002Fphp-jwt",", about 130 lines).",[365,389,390],{},"A conformance checklist to test your client against.",[358,392,393,394,398,399,402],{},"Along with this page you need the ",[395,396,397],"a",{"href":121},"API reference"," for routes and fields, and the\n",[395,400,401],{"href":43},"error reference"," for codes.",[404,405,407],"h2",{"id":406},"the-rules","The rules",[409,410,412],"h3",{"id":411},"rule-1-you-sign-your-own-token","Rule 1: you sign your own token",[358,414,415,416,419],{},"There is no token endpoint. You create a JSON Web Token yourself and sign it with your ",[372,417,418],{},"apiSecret",":",[421,422,423,439],"table",{},[424,425,426],"thead",{},[427,428,429,433,436],"tr",{},[430,431,432],"th",{},"Part",[430,434,435],{},"Field",[430,437,438],{},"Value",[440,441,442,459,474,498,515,531,554],"tbody",{},[427,443,444,448,453],{},[445,446,447],"td",{},"Header",[445,449,450],{},[372,451,452],{},"alg",[445,454,455,458],{},[372,456,457],{},"HS256",". No other algorithm is accepted.",[427,460,461,463,468],{},[445,462,447],{},[445,464,465],{},[372,466,467],{},"typ",[445,469,470,473],{},[372,471,472],{},"JWT",". Usual, but not checked.",[427,475,476,478,483],{},[445,477,447],{},[445,479,480],{},[372,481,482],{},"kid",[445,484,485,486,489,490,493,494,497],{},"Your ",[372,487,488],{},"apiKey",", exactly as issued: ",[372,491,492],{},"key_"," followed by 24 characters from ",[372,495,496],{},"A–Z a–z 0–9 _ -",".",[427,499,500,503,508],{},[445,501,502],{},"Payload",[445,504,505],{},[372,506,507],{},"sub",[445,509,485,510,512,513,497],{},[372,511,488],{}," again. It must equal ",[372,514,482],{},[427,516,517,519,524],{},[445,518,502],{},[445,520,521],{},[372,522,523],{},"iat",[445,525,526,527],{},"Issued-at time, in whole seconds since the Unix epoch. ",[528,529,530],"strong",{},"Required.",[427,532,533,535,540],{},[445,534,502],{},[445,536,537],{},[372,538,539],{},"exp",[445,541,542,543,546,547,549,550,553],{},"Expiry time, in seconds since the epoch. ",[528,544,545],{},"Required",", after ",[372,548,523],{},", and at most ",[528,551,552],{},"3600"," seconds after it.",[427,555,556,558,563],{},[445,557,502],{},[445,559,560],{},[372,561,562],{},"nbf",[445,564,565],{},"Optional. If you set it, it's enforced with the same 30-second tolerance. Most clients leave it out.",[358,567,568,569,572,573,576,577,579,580,582,583,585],{},"Send the token as ",[372,570,571],{},"Authorization: Bearer \u003Ctoken>",". The server allows ",[528,574,575],{},"30 seconds"," of clock\ndifference: a token is still accepted up to 30 seconds after its ",[372,578,539],{},", and its ",[372,581,523],{}," may be up to\n30 seconds ahead of the server's clock. An ",[372,584,523],{}," further ahead is refused, so keep your server\nclock synchronized.",[358,587,588,591,592,594],{},[528,589,590],{},"Reuse a token"," until about a minute before its ",[372,593,539],{},", then sign a new one. Signing is cheap,\nbut a token per request adds nothing. Each of your servers can sign its own token: the rate limit\nbelongs to your account, not to a token.",[358,596,597,604,605,608,609,611,612,615,616,497],{},[528,598,599,600,603],{},"Every authentication failure is the same ",[372,601,602],{},"401 unauthorized",","," with the same message:\n",[372,606,607],{},"Authentication is required or the provided credentials are invalid."," A missing header, a wrong\nkey, a deactivated account, a bad signature, a missing ",[372,610,523],{},", an expired token, an over-long token\nand a revoked token all look alike. The server doesn't say which check failed, so a leaked key or\ntoken tells whoever found it nothing. Check your token against the table above, and quote the\n",[372,613,614],{},"request_id"," when you ask for help. See ",[395,617,19],{"href":20},[358,619,620,621,626,627,630,631,634,635,638,639,375,642,645],{},"To end a token early, call ",[395,622,623],{"href":140},[372,624,625],{},"POST \u002Fv1\u002Fapi-account\u002Frevoke-token",",\nauthenticated with that token. It needs the same ",[372,628,629],{},"api\u002F*"," permission on mto as ",[372,632,633],{},"validate-me",". It\nanswers ",[372,636,637],{},"200"," with the message ",[372,640,641],{},"Token revoked successfully",[372,643,644],{},"data.expiresIn",": how many seconds\nthe revocation is held, which is the token's remaining lifetime plus the 30-second tolerance. The\ntoken is refused from then on, and your other tokens keep working.",[409,647,649],{"id":648},"rule-2-every-json-response-has-an-envelope","Rule 2: every JSON response has an envelope",[358,651,652],{},"A successful response looks like this (the student is shortened to two of its fields):",[654,655,660],"pre",{"className":656,"code":657,"language":658,"meta":659,"style":659},"language-json shiki shiki-themes github-light-high-contrast github-dark-high-contrast","{\n  \"success\": true,\n  \"message\": \"Students fetched successfully.\",\n  \"data\": [ { \"_id\": \"66f2b7c1e4a9d20012ab34cd\", \"username\": \"XXK10427\" } ],\n  \"pagination\": { \"page\": 1, \"limit\": 20, \"total\": 1, \"totalPages\": 1 }\n}\n","json","",[372,661,662,671,688,702,733,781],{"__ignoreMap":659},[663,664,667],"span",{"class":665,"line":666},"line",1,[663,668,670],{"class":669},"suds8","{\n",[663,672,674,678,681,685],{"class":665,"line":673},2,[663,675,677],{"class":676},"sne4z","  \"success\"",[663,679,680],{"class":669},": ",[663,682,684],{"class":683},"s-5SL","true",[663,686,687],{"class":669},",\n",[663,689,691,694,696,700],{"class":665,"line":690},3,[663,692,693],{"class":676},"  \"message\"",[663,695,680],{"class":669},[663,697,699],{"class":698},"sT6z2","\"Students fetched successfully.\"",[663,701,687],{"class":669},[663,703,705,708,711,714,716,719,722,725,727,730],{"class":665,"line":704},4,[663,706,707],{"class":676},"  \"data\"",[663,709,710],{"class":669},": [ { ",[663,712,713],{"class":676},"\"_id\"",[663,715,680],{"class":669},[663,717,718],{"class":698},"\"66f2b7c1e4a9d20012ab34cd\"",[663,720,721],{"class":669},", ",[663,723,724],{"class":676},"\"username\"",[663,726,680],{"class":669},[663,728,729],{"class":698},"\"XXK10427\"",[663,731,732],{"class":669}," } ],\n",[663,734,736,739,742,745,747,750,752,755,757,760,762,765,767,769,771,774,776,778],{"class":665,"line":735},5,[663,737,738],{"class":676},"  \"pagination\"",[663,740,741],{"class":669},": { ",[663,743,744],{"class":676},"\"page\"",[663,746,680],{"class":669},[663,748,749],{"class":683},"1",[663,751,721],{"class":669},[663,753,754],{"class":676},"\"limit\"",[663,756,680],{"class":669},[663,758,759],{"class":683},"20",[663,761,721],{"class":669},[663,763,764],{"class":676},"\"total\"",[663,766,680],{"class":669},[663,768,749],{"class":683},[663,770,721],{"class":669},[663,772,773],{"class":676},"\"totalPages\"",[663,775,680],{"class":669},[663,777,749],{"class":683},[663,779,780],{"class":669}," }\n",[663,782,784],{"class":665,"line":783},6,[663,785,786],{"class":669},"}\n",[358,788,789,792],{},[372,790,791],{},"pagination"," appears on lists only. An error looks like this:",[654,794,796],{"className":656,"code":795,"language":658,"meta":659,"style":659},"{\n  \"error\": {\n    \"code\": \"conflict\",\n    \"message\": \"A student with that email is already registered to this account (66f2b7c1e4a9d20012ab34cd).\",\n    \"documentation_url\": \"https:\u002F\u002Fhub.main-team.org\u002Fapi\u002Ferrors#conflict\",\n    \"request_id\": \"3f1c9a52-7d3e-4a8b-9e51-0c2d4b6a8f10\"\n  }\n}\n",[372,797,798,802,810,822,834,846,856,862],{"__ignoreMap":659},[663,799,800],{"class":665,"line":666},[663,801,670],{"class":669},[663,803,804,807],{"class":665,"line":673},[663,805,806],{"class":676},"  \"error\"",[663,808,809],{"class":669},": {\n",[663,811,812,815,817,820],{"class":665,"line":690},[663,813,814],{"class":676},"    \"code\"",[663,816,680],{"class":669},[663,818,819],{"class":698},"\"conflict\"",[663,821,687],{"class":669},[663,823,824,827,829,832],{"class":665,"line":704},[663,825,826],{"class":676},"    \"message\"",[663,828,680],{"class":669},[663,830,831],{"class":698},"\"A student with that email is already registered to this account (66f2b7c1e4a9d20012ab34cd).\"",[663,833,687],{"class":669},[663,835,836,839,841,844],{"class":665,"line":735},[663,837,838],{"class":676},"    \"documentation_url\"",[663,840,680],{"class":669},[663,842,843],{"class":698},"\"https:\u002F\u002Fhub.main-team.org\u002Fapi\u002Ferrors#conflict\"",[663,845,687],{"class":669},[663,847,848,851,853],{"class":665,"line":783},[663,849,850],{"class":676},"    \"request_id\"",[663,852,680],{"class":669},[663,854,855],{"class":698},"\"3f1c9a52-7d3e-4a8b-9e51-0c2d4b6a8f10\"\n",[663,857,859],{"class":665,"line":858},7,[663,860,861],{"class":669},"  }\n",[663,863,865],{"class":665,"line":864},8,[663,866,786],{"class":669},[358,868,869,872,873,878],{},[528,870,871],{},"One route has no envelope."," ",[395,874,875],{"href":144},[372,876,877],{},"GET \u002Fv1\u002Fapi-account\u002Fvalidate-me","\nreturns the account object itself.",[358,880,881,882,885,886,889,890,375,892,497],{},"Branch on the HTTP status and ",[372,883,884],{},"error.code",". ",[372,887,888],{},"message"," is written for people and can change. See\n",[395,891,33],{"href":34},[395,893,42],{"href":43},[358,895,896,903,904,497],{},[528,897,898,899,902],{},"A single read answers ",[372,900,901],{},"404 not_found"," when nothing matches."," A student that doesn't exist and one\nthat isn't yours answer alike, and so does every other record your account can't see. A malformed\nid is ",[372,905,906],{},"400 bad_request",[409,908,910],{"id":909},"rule-3-every-response-carries-a-request-id","Rule 3: every response carries a request id",[358,912,913,914,917,918,921,922,924,925,928],{},"Every response, errors included, has an ",[372,915,916],{},"X-Request-Id"," header, and an error body repeats it as\n",[372,919,920],{},"error.request_id",". You can send your own ",[372,923,916],{}," and the API will use it, if it is 1 to 256\ncharacters from ",[372,926,927],{},"A–Z a–z 0–9 . _ : ; = + \u002F @ -"," (a UUID works). Anything else is ignored, and the\nAPI assigns an id itself. An assigned id isn't always a UUID, so store it as an opaque string. Log\nit next to your own request log, and quote it in a support request.",[409,930,932],{"id":931},"rule-4-paths-ids-and-bodies","Rule 4: paths, ids and bodies",[934,935,936,952,971,978,1007],"ul",{},[365,937,938,939,942,943,946,947,951],{},"Every route is under ",[372,940,941],{},"https:\u002F\u002Fapi.main-team.org\u002Fv1"," in production and\n",[372,944,945],{},"https:\u002F\u002Fapisnd.main-team.org\u002Fv1"," in the ",[395,948,950],{"href":949},"\u002Fapi\u002Fenvironments#sandbox","sandbox",". HTTPS only.",[365,953,954,955,958,959,964,965,967,968,497],{},"Organization routes take the organization's ",[372,956,957],{},"_id",", a 24-character hexadecimal id from\n",[395,960,961],{"href":162},[372,962,963],{},"GET \u002Fv1\u002Forganization",", never its slug. Look the ids up once\nand cache them. An id that names no organization answers ",[372,966,901],{}," with\n",[372,969,970],{},"Organization not found!",[365,972,973,974,977],{},"Routes without an organization in the path act on ",[528,975,976],{},"mto",", the core record.",[365,979,980,981,984,985,988,989,992,993,996,997,721,1000,1003,1004,497],{},"Send bodies as UTF-8 JSON with ",[372,982,983],{},"Content-Type: application\u002Fjson",", at most ",[528,986,987],{},"100 kB",". A larger\nbody is refused with ",[372,990,991],{},"413 payload_too_large"," before the token is even read. A character set or\ncontent encoding the API can't read is ",[372,994,995],{},"415 unsupported_media_type",". Uncompressed bodies are\naccepted, and so are bodies sent with ",[372,998,999],{},"Content-Encoding: gzip",[372,1001,1002],{},"deflate"," or ",[372,1005,1006],{},"br",[365,1008,1009,1010,1012],{},"Send only the fields the reference documents. Any other property is ",[372,1011,906],{},", and the\nmessage names it.",[409,1014,1016],{"id":1015},"rule-5-lists-are-paginated","Rule 5: lists are paginated",[358,1018,1019,1022,1023,1026,1027,1029,1030,885,1033,1036,1037,1039,1040,497],{},[372,1020,1021],{},"page"," starts at 1. ",[372,1024,1025],{},"limit"," defaults to 20 and is capped at 100. Values outside that range are\nclamped, not refused, and a value that isn't a number falls back to the default. Walk pages until\n",[372,1028,1021],{}," reaches ",[372,1031,1032],{},"pagination.totalPages",[372,1034,1035],{},"totalPages"," is 0 when the list is empty. Records can be\nadded between your requests, so de-duplicate by ",[372,1038,957],{}," when you walk a long list. See\n",[395,1041,39],{"href":40},[409,1043,1045],{"id":1044},"rule-6-downloads-are-not-json","Rule 6: downloads are not JSON",[358,1047,1048,375,1051,1054],{},[395,1049,1050],{"href":261},"Certificate",[395,1052,1053],{"href":267},"report","\ndownloads return the file itself, with no envelope:",[934,1056,1057,1066,1087,1093,1107],{},[365,1058,1059,1062,1063,497],{},[372,1060,1061],{},"Content-Type"," is the stored type, usually ",[372,1064,1065],{},"application\u002Fpdf",[365,1067,1068,1071,1072,1075,1076,1079,1080,1083,1084,1086],{},[372,1069,1070],{},"Content-Disposition"," is ",[372,1073,1074],{},"attachment; filename=\"\u003Cascii>\"; filename*=UTF-8''\u003Cpercent-encoded>",".\nPrefer ",[372,1077,1078],{},"filename*",", which carries the real name, including non-ASCII characters. Fall back to\n",[372,1081,1082],{},"filename"," only when ",[372,1085,1078],{}," is missing. Either way, strip any path before you write the\nfile.",[365,1088,1089,1092],{},[372,1090,1091],{},"Content-Length"," is present when the size is known.",[365,1094,1095,1096,1099,1100,1003,1103,1106],{},"An error arrives ",[528,1097,1098],{},"before"," any bytes, as the usual JSON error with a ",[372,1101,1102],{},"4xx",[372,1104,1105],{},"5xx"," status.\nCheck the status before you write anything.",[365,1108,1109],{},"If the connection closes partway through the body, the download failed. Discard the partial\nfile and try again.",[409,1111,1113],{"id":1112},"rule-7-403-is-a-permission","Rule 7: 403 is a permission",[358,1115,1116,1119,1120,497],{},[372,1117,1118],{},"403 forbidden"," means your token is valid but your account lacks the permission for that route on\nthat organization. Re-signing or retrying won't change that; ask your operator for the role. A few\nroutes use 403 for a rule of their own and say so in the message, such as a sign-in link for a\nstudent with no access to that organization. See ",[395,1121,25],{"href":26},[409,1123,1125],{"id":1124},"rule-8-429-means-wait","Rule 8: 429 means wait",[358,1127,1128,1129,1132],{},"Each account may make ",[528,1130,1131],{},"100 requests per 60 seconds to each operation",", wherever the requests\ncome from, and each operation is counted on its own. Counted responses carry:",[421,1134,1135,1144],{},[424,1136,1137],{},[427,1138,1139,1141],{},[430,1140,447],{},[430,1142,1143],{},"Meaning",[440,1145,1146,1156,1166],{},[427,1147,1148,1153],{},[445,1149,1150],{},[372,1151,1152],{},"X-RateLimit-Limit",[445,1154,1155],{},"Requests allowed per window for this operation.",[427,1157,1158,1163],{},[445,1159,1160],{},[372,1161,1162],{},"X-RateLimit-Remaining",[445,1164,1165],{},"Requests left in the current window.",[427,1167,1168,1173],{},[445,1169,1170],{},[372,1171,1172],{},"X-RateLimit-Reset",[445,1174,1175],{},"Seconds until the window resets.",[358,1177,1178,1179,1182,1183,1186,1187,1190,1191,1194,1195,1198,1199,497],{},"A ",[372,1180,1181],{},"429 too_many_requests"," carries ",[372,1184,1185],{},"Retry-After",", in seconds. Wait that long. Requests sent sooner\nare refused too, though they don't extend the block, and a block never lasts longer than one\nwindow. Requests refused before they're counted don't use up your budget. These are a ",[372,1188,1189],{},"401",", a\n",[372,1192,1193],{},"403"," for a missing permission, an unknown organization, and a body refused as too large or\nunreadable. A request refused later, such as a ",[372,1196,1197],{},"400"," for a bad field, is counted. See\n",[395,1200,45],{"href":46},[409,1202,1204],{"id":1203},"rule-9-retry-only-what-is-safe","Rule 9: retry only what is safe",[421,1206,1207,1217],{},[424,1208,1209],{},[427,1210,1211,1214],{},[430,1212,1213],{},"Request",[430,1215,1216],{},"Safe to retry?",[440,1218,1219,1229,1243,1253,1268,1281,1298],{},[427,1220,1221,1226],{},[445,1222,1223,1224],{},"Any ",[372,1225,133],{},[445,1227,1228],{},"Yes.",[427,1230,1231,1236],{},[445,1232,1233],{},[372,1234,1235],{},"POST \u002Fv1\u002F{organizationId}\u002Fapplication",[445,1237,1238,1239,1242],{},"Yes, for the same exam: a repeat answers ",[372,1240,1241],{},"200 \"Application already exists.\""," with the existing application.",[427,1244,1245,1250],{},[445,1246,1247,1249],{},[372,1248,190],{}," on a student",[445,1251,1252],{},"Yes: repeating it writes the same values.",[427,1254,1255,1260],{},[445,1256,1257],{},[372,1258,1259],{},"PUT \u002Fv1\u002F{organizationId}\u002Fapplication\u002F{applicationId}",[445,1261,1262,1263,872,1265],{},"Yes: once the move has happened, a repeat answers ",[372,1264,637],{},[372,1266,1267],{},"\"Application already uses that exam.\"",[427,1269,1270,1275],{},[445,1271,1272],{},[372,1273,1274],{},"DELETE \u002Fv1\u002F{organizationId}\u002Fapplication\u002F{applicationId}",[445,1276,1277,1278,1280],{},"Yes, but the second call answers ",[372,1279,901],{}," because the first one worked.",[427,1282,1283,1288],{},[445,1284,1285],{},[372,1286,1287],{},"POST \u002Fv1\u002Fstudent",[445,1289,1290,1293,1294,1297],{},[528,1291,1292],{},"No."," A repeat answers ",[372,1295,1296],{},"409 conflict"," naming the existing student. Fetch that student instead.",[427,1299,1300,1305],{},[445,1301,1302],{},[372,1303,1304],{},"POST \u002Fv1\u002F{organizationId}\u002Fauth\u002Fsignin",[445,1306,1307,1308,1311],{},"Mint a new link instead. ",[528,1309,1310],{},"Never"," retry, fetch or prefetch a link URL: it works once.",[358,1313,1314,1315,1317,1318,1320,1321,1324,1325,1327,1328,1330,1331,497],{},"Retry network errors and ",[372,1316,1105],{}," answers, on requests that are safe to repeat, with exponential\nbackoff and jitter. Don't retry ",[372,1319,1102],{},"\nanswers, with two exceptions: ",[372,1322,1323],{},"429"," after ",[372,1326,1185],{},", and a ",[372,1329,1189],{}," once after signing a fresh\ntoken, in case yours expired mid-flight. See\n",[395,1332,1333],{"href":49},"Retries and idempotency",[409,1335,1337],{"id":1336},"rule-10-server-side-only","Rule 10: server-side only",[358,1339,1340,1341,1343],{},"In production the API sends no CORS headers, so browsers on other origins can't call it. Keep\nyour client, and your ",[372,1342,418],{},", on your servers.",[404,1345,1347],{"id":1346},"a-minimal-client-in-nodejs","A minimal client in Node.js",[358,1349,1350,1351,1353],{},"About 90 lines. It needs Node.js 20 or newer for the built-in ",[372,1352,374],{},", and one dependency:",[654,1355,1359],{"className":1356,"code":1357,"language":1358,"meta":659,"style":659},"language-bash shiki shiki-themes github-light-high-contrast github-dark-high-contrast","npm install jsonwebtoken\n","bash",[372,1360,1361],{"__ignoreMap":659},[663,1362,1363,1367,1370],{"class":665,"line":666},[663,1364,1366],{"class":1365},"soyes","npm",[663,1368,1369],{"class":698}," install",[663,1371,1372],{"class":698}," jsonwebtoken\n",[654,1374,1378],{"className":1375,"code":1376,"language":1377,"meta":659,"style":659},"language-js shiki shiki-themes github-light-high-contrast github-dark-high-contrast","\u002F\u002F main-team-client.mjs: a minimal Main Team API client (Node.js 20+).\nimport jwt from 'jsonwebtoken';\nimport { randomUUID } from 'node:crypto';\nimport { writeFile } from 'node:fs\u002Fpromises';\nimport { basename, join } from 'node:path';\n\nconst BASE_URL = 'https:\u002F\u002Fapi.main-team.org\u002Fv1'; \u002F\u002F sandbox: 'https:\u002F\u002Fapisnd.main-team.org\u002Fv1'\nconst TOKEN_LIFETIME = 900; \u002F\u002F seconds; the API accepts at most 3600\nconst RENEW_BEFORE = 60;    \u002F\u002F sign a new token this long before exp\n\nexport class ApiError extends Error {\n  constructor(status, body, headers) {\n    super(body?.error?.message ?? `HTTP ${status}`);\n    this.status = status;\n    this.code = body?.error?.code ?? 'unknown';\n    this.documentationUrl = body?.error?.documentation_url;\n    this.requestId = body?.error?.request_id ?? headers.get('x-request-id');\n    this.retryAfter = Number(headers.get('retry-after')) || undefined;\n  }\n}\n\nexport class MainTeamClient {\n  #apiKey; #apiSecret; #token = null; #tokenExp = 0;\n\n  constructor({ apiKey, apiSecret }) {\n    if (!\u002F^key_[A-Za-z0-9_-]{24}$\u002F.test(apiKey ?? '')) throw new Error('apiKey is not in the issued format');\n    if (!apiSecret) throw new Error('apiSecret is missing');\n    this.#apiKey = apiKey;\n    this.#apiSecret = apiSecret;\n  }\n\n  #bearer() {\n    const now = Math.floor(Date.now() \u002F 1000);\n    if (!this.#token || now >= this.#tokenExp - RENEW_BEFORE) {\n      this.#tokenExp = now + TOKEN_LIFETIME;\n      this.#token = jwt.sign(\n        { sub: this.#apiKey, iat: now, exp: this.#tokenExp }, \u002F\u002F iat and exp are both required\n        this.#apiSecret,\n        { algorithm: 'HS256', keyid: this.#apiKey },          \u002F\u002F keyid becomes the kid header\n      );\n    }\n    return this.#token;\n  }\n\n  async #send(method, path, { query, body, timeoutMs = 30_000 } = {}) {\n    const url = new URL(BASE_URL + path);\n    for (const [key, value] of Object.entries(query ?? {})) url.searchParams.set(key, String(value));\n    const res = await fetch(url, {\n      method,\n      headers: {\n        Authorization: `Bearer ${this.#bearer()}`,\n        'X-Request-Id': randomUUID(),\n        ...(body === undefined ? {} : { 'Content-Type': 'application\u002Fjson' }),\n      },\n      body: body === undefined ? undefined : JSON.stringify(body),\n      signal: AbortSignal.timeout(timeoutMs),\n    });\n    if (!res.ok) throw new ApiError(res.status, await res.json().catch(() => null), res.headers);\n    return res;\n  }\n\n  \u002F** A JSON route. Resolves to the envelope: { success, message, data, pagination? }. *\u002F\n  async request(method, path, options) {\n    const json = await (await this.#send(method, path, options)).json();\n    \u002F\u002F validate-me is the one JSON route that answers without an envelope.\n    return path === '\u002Fapi-account\u002Fvalidate-me' ? { success: true, data: json } : json;\n  }\n\n  \u002F** Every item of a paginated list, 100 per page (the maximum). *\u002F\n  async *paginate(path, query = {}) {\n    for (let page = 1; ; page++) {\n      const { data, pagination } = await this.request('GET', path, { query: { ...query, page, limit: 100 } });\n      yield* data;\n      if (!pagination || page >= pagination.totalPages) return;\n    }\n  }\n\n  \u002F** A certificate or report download. Saves the file and resolves to its name. *\u002F\n  async download(path, directory = '.') {\n    const res = await this.#send('GET', path, { timeoutMs: 120_000 });\n    const bytes = Buffer.from(await res.arrayBuffer()); \u002F\u002F rejects if the connection drops mid-body\n    const expected = Number(res.headers.get('content-length'));\n    const decoded = Boolean(res.headers.get('content-encoding')); \u002F\u002F then Content-Length is the encoded size\n    if (expected && !decoded && bytes.length !== expected) throw new Error('Download ended early');\n    const name = basename(fileNameOf(res.headers.get('content-disposition')) ?? 'download.pdf');\n    await writeFile(join(directory, name), bytes);\n    return name;\n  }\n}\n\nfunction fileNameOf(header) {\n  const star = \u002Ffilename\\*=UTF-8''([^;]+)\u002Fi.exec(header ?? '');\n  if (star) return decodeURIComponent(star[1]);\n  return \u002Ffilename=\"([^\"]*)\"\u002Fi.exec(header ?? '')?.[1] ?? null;\n}\n","js",[372,1379,1380,1386,1404,1418,1432,1446,1452,1472,1489,1508,1513,1534,1559,1582,1597,1617,1630,1659,1693,1698,1703,1708,1720,1753,1758,1775,1833,1858,1871,1884,1889,1894,1906,1939,1975,1994,2012,2031,2040,2060,2066,2072,2083,2088,2093,2143,2169,2222,2241,2247,2253,2276,2290,2326,2332,2360,2372,2378,2424,2432,2437,2442,2448,2471,2502,2508,2536,2541,2546,2552,2575,2601,2649,2658,2685,2690,2695,2700,2706,2730,2760,2790,2815,2843,2887,2923,2940,2948,2953,2958,2963,2979,3030,3052,3099],{"__ignoreMap":659},[663,1381,1382],{"class":665,"line":666},[663,1383,1385],{"class":1384},"sLBg1","\u002F\u002F main-team-client.mjs: a minimal Main Team API client (Node.js 20+).\n",[663,1387,1388,1392,1395,1398,1401],{"class":665,"line":673},[663,1389,1391],{"class":1390},"sHUrx","import",[663,1393,1394],{"class":669}," jwt ",[663,1396,1397],{"class":1390},"from",[663,1399,1400],{"class":698}," 'jsonwebtoken'",[663,1402,1403],{"class":669},";\n",[663,1405,1406,1408,1411,1413,1416],{"class":665,"line":690},[663,1407,1391],{"class":1390},[663,1409,1410],{"class":669}," { randomUUID } ",[663,1412,1397],{"class":1390},[663,1414,1415],{"class":698}," 'node:crypto'",[663,1417,1403],{"class":669},[663,1419,1420,1422,1425,1427,1430],{"class":665,"line":704},[663,1421,1391],{"class":1390},[663,1423,1424],{"class":669}," { writeFile } ",[663,1426,1397],{"class":1390},[663,1428,1429],{"class":698}," 'node:fs\u002Fpromises'",[663,1431,1403],{"class":669},[663,1433,1434,1436,1439,1441,1444],{"class":665,"line":735},[663,1435,1391],{"class":1390},[663,1437,1438],{"class":669}," { basename, join } ",[663,1440,1397],{"class":1390},[663,1442,1443],{"class":698}," 'node:path'",[663,1445,1403],{"class":669},[663,1447,1448],{"class":665,"line":783},[663,1449,1451],{"emptyLinePlaceholder":1450},true,"\n",[663,1453,1454,1457,1460,1463,1466,1469],{"class":665,"line":858},[663,1455,1456],{"class":1390},"const",[663,1458,1459],{"class":683}," BASE_URL",[663,1461,1462],{"class":1390}," =",[663,1464,1465],{"class":698}," 'https:\u002F\u002Fapi.main-team.org\u002Fv1'",[663,1467,1468],{"class":669},"; ",[663,1470,1471],{"class":1384},"\u002F\u002F sandbox: 'https:\u002F\u002Fapisnd.main-team.org\u002Fv1'\n",[663,1473,1474,1476,1479,1481,1484,1486],{"class":665,"line":864},[663,1475,1456],{"class":1390},[663,1477,1478],{"class":683}," TOKEN_LIFETIME",[663,1480,1462],{"class":1390},[663,1482,1483],{"class":683}," 900",[663,1485,1468],{"class":669},[663,1487,1488],{"class":1384},"\u002F\u002F seconds; the API accepts at most 3600\n",[663,1490,1492,1494,1497,1499,1502,1505],{"class":665,"line":1491},9,[663,1493,1456],{"class":1390},[663,1495,1496],{"class":683}," RENEW_BEFORE",[663,1498,1462],{"class":1390},[663,1500,1501],{"class":683}," 60",[663,1503,1504],{"class":669},";    ",[663,1506,1507],{"class":1384},"\u002F\u002F sign a new token this long before exp\n",[663,1509,1511],{"class":665,"line":1510},10,[663,1512,1451],{"emptyLinePlaceholder":1450},[663,1514,1516,1519,1522,1525,1528,1531],{"class":665,"line":1515},11,[663,1517,1518],{"class":1390},"export",[663,1520,1521],{"class":1390}," class",[663,1523,1524],{"class":1365}," ApiError",[663,1526,1527],{"class":1390}," extends",[663,1529,1530],{"class":683}," Error",[663,1532,1533],{"class":669}," {\n",[663,1535,1537,1540,1543,1546,1548,1551,1553,1556],{"class":665,"line":1536},12,[663,1538,1539],{"class":1390},"  constructor",[663,1541,1542],{"class":669},"(",[663,1544,1545],{"class":1365},"status",[663,1547,721],{"class":669},[663,1549,1550],{"class":1365},"body",[663,1552,721],{"class":669},[663,1554,1555],{"class":1365},"headers",[663,1557,1558],{"class":669},") {\n",[663,1560,1562,1565,1568,1571,1574,1576,1579],{"class":665,"line":1561},13,[663,1563,1564],{"class":683},"    super",[663,1566,1567],{"class":669},"(body?.error?.message ",[663,1569,1570],{"class":1390},"??",[663,1572,1573],{"class":698}," `HTTP ${",[663,1575,1545],{"class":669},[663,1577,1578],{"class":698},"}`",[663,1580,1581],{"class":669},");\n",[663,1583,1585,1588,1591,1594],{"class":665,"line":1584},14,[663,1586,1587],{"class":683},"    this",[663,1589,1590],{"class":669},".status ",[663,1592,1593],{"class":1390},"=",[663,1595,1596],{"class":669}," status;\n",[663,1598,1600,1602,1605,1607,1610,1612,1615],{"class":665,"line":1599},15,[663,1601,1587],{"class":683},[663,1603,1604],{"class":669},".code ",[663,1606,1593],{"class":1390},[663,1608,1609],{"class":669}," body?.error?.code ",[663,1611,1570],{"class":1390},[663,1613,1614],{"class":698}," 'unknown'",[663,1616,1403],{"class":669},[663,1618,1620,1622,1625,1627],{"class":665,"line":1619},16,[663,1621,1587],{"class":683},[663,1623,1624],{"class":669},".documentationUrl ",[663,1626,1593],{"class":1390},[663,1628,1629],{"class":669}," body?.error?.documentation_url;\n",[663,1631,1633,1635,1638,1640,1643,1645,1648,1652,1654,1657],{"class":665,"line":1632},17,[663,1634,1587],{"class":683},[663,1636,1637],{"class":669},".requestId ",[663,1639,1593],{"class":1390},[663,1641,1642],{"class":669}," body?.error?.request_id ",[663,1644,1570],{"class":1390},[663,1646,1647],{"class":669}," headers.",[663,1649,1651],{"class":1650},"sKwhi","get",[663,1653,1542],{"class":669},[663,1655,1656],{"class":698},"'x-request-id'",[663,1658,1581],{"class":669},[663,1660,1662,1664,1667,1669,1672,1675,1677,1679,1682,1685,1688,1691],{"class":665,"line":1661},18,[663,1663,1587],{"class":683},[663,1665,1666],{"class":669},".retryAfter ",[663,1668,1593],{"class":1390},[663,1670,1671],{"class":1650}," Number",[663,1673,1674],{"class":669},"(headers.",[663,1676,1651],{"class":1650},[663,1678,1542],{"class":669},[663,1680,1681],{"class":698},"'retry-after'",[663,1683,1684],{"class":669},")) ",[663,1686,1687],{"class":1390},"||",[663,1689,1690],{"class":683}," undefined",[663,1692,1403],{"class":669},[663,1694,1696],{"class":665,"line":1695},19,[663,1697,861],{"class":669},[663,1699,1701],{"class":665,"line":1700},20,[663,1702,786],{"class":669},[663,1704,1706],{"class":665,"line":1705},21,[663,1707,1451],{"emptyLinePlaceholder":1450},[663,1709,1711,1713,1715,1718],{"class":665,"line":1710},22,[663,1712,1518],{"class":1390},[663,1714,1521],{"class":1390},[663,1716,1717],{"class":1365}," MainTeamClient",[663,1719,1533],{"class":669},[663,1721,1723,1726,1728,1731,1733,1736,1738,1741,1743,1746,1748,1751],{"class":665,"line":1722},23,[663,1724,1725],{"class":1365},"  #apiKey",[663,1727,1468],{"class":669},[663,1729,1730],{"class":1365},"#apiSecret",[663,1732,1468],{"class":669},[663,1734,1735],{"class":1365},"#token",[663,1737,1462],{"class":1390},[663,1739,1740],{"class":683}," null",[663,1742,1468],{"class":669},[663,1744,1745],{"class":1365},"#tokenExp",[663,1747,1462],{"class":1390},[663,1749,1750],{"class":683}," 0",[663,1752,1403],{"class":669},[663,1754,1756],{"class":665,"line":1755},24,[663,1757,1451],{"emptyLinePlaceholder":1450},[663,1759,1761,1763,1766,1768,1770,1772],{"class":665,"line":1760},25,[663,1762,1539],{"class":1390},[663,1764,1765],{"class":669},"({ ",[663,1767,488],{"class":1365},[663,1769,721],{"class":669},[663,1771,418],{"class":1365},[663,1773,1774],{"class":669}," }) {\n",[663,1776,1778,1781,1784,1787,1790,1793,1795,1798,1801,1803,1805,1808,1811,1813,1816,1818,1821,1824,1826,1828,1831],{"class":665,"line":1777},26,[663,1779,1780],{"class":1390},"    if",[663,1782,1783],{"class":669}," (",[663,1785,1786],{"class":1390},"!",[663,1788,1789],{"class":698},"\u002F",[663,1791,1792],{"class":1390},"^",[663,1794,492],{"class":698},[663,1796,1797],{"class":683},"[A-Za-z0-9_-]",[663,1799,1800],{"class":1390},"{24}$",[663,1802,1789],{"class":698},[663,1804,497],{"class":669},[663,1806,1807],{"class":1650},"test",[663,1809,1810],{"class":669},"(apiKey ",[663,1812,1570],{"class":1390},[663,1814,1815],{"class":698}," ''",[663,1817,1684],{"class":669},[663,1819,1820],{"class":1390},"throw",[663,1822,1823],{"class":1390}," new",[663,1825,1530],{"class":1650},[663,1827,1542],{"class":669},[663,1829,1830],{"class":698},"'apiKey is not in the issued format'",[663,1832,1581],{"class":669},[663,1834,1836,1838,1840,1842,1845,1847,1849,1851,1853,1856],{"class":665,"line":1835},27,[663,1837,1780],{"class":1390},[663,1839,1783],{"class":669},[663,1841,1786],{"class":1390},[663,1843,1844],{"class":669},"apiSecret) ",[663,1846,1820],{"class":1390},[663,1848,1823],{"class":1390},[663,1850,1530],{"class":1650},[663,1852,1542],{"class":669},[663,1854,1855],{"class":698},"'apiSecret is missing'",[663,1857,1581],{"class":669},[663,1859,1861,1863,1866,1868],{"class":665,"line":1860},28,[663,1862,1587],{"class":683},[663,1864,1865],{"class":669},".#apiKey ",[663,1867,1593],{"class":1390},[663,1869,1870],{"class":669}," apiKey;\n",[663,1872,1874,1876,1879,1881],{"class":665,"line":1873},29,[663,1875,1587],{"class":683},[663,1877,1878],{"class":669},".#apiSecret ",[663,1880,1593],{"class":1390},[663,1882,1883],{"class":669}," apiSecret;\n",[663,1885,1887],{"class":665,"line":1886},30,[663,1888,861],{"class":669},[663,1890,1892],{"class":665,"line":1891},31,[663,1893,1451],{"emptyLinePlaceholder":1450},[663,1895,1897,1900,1903],{"class":665,"line":1896},32,[663,1898,1899],{"class":669},"  #",[663,1901,1902],{"class":1650},"bearer",[663,1904,1905],{"class":669},"() {\n",[663,1907,1909,1912,1915,1917,1920,1923,1926,1929,1932,1934,1937],{"class":665,"line":1908},33,[663,1910,1911],{"class":1390},"    const",[663,1913,1914],{"class":683}," now",[663,1916,1462],{"class":1390},[663,1918,1919],{"class":669}," Math.",[663,1921,1922],{"class":1650},"floor",[663,1924,1925],{"class":669},"(Date.",[663,1927,1928],{"class":1650},"now",[663,1930,1931],{"class":669},"() ",[663,1933,1789],{"class":1390},[663,1935,1936],{"class":683}," 1000",[663,1938,1581],{"class":669},[663,1940,1942,1944,1946,1948,1951,1954,1956,1959,1962,1965,1968,1971,1973],{"class":665,"line":1941},34,[663,1943,1780],{"class":1390},[663,1945,1783],{"class":669},[663,1947,1786],{"class":1390},[663,1949,1950],{"class":683},"this",[663,1952,1953],{"class":669},".#token ",[663,1955,1687],{"class":1390},[663,1957,1958],{"class":669}," now ",[663,1960,1961],{"class":1390},">=",[663,1963,1964],{"class":683}," this",[663,1966,1967],{"class":669},".#tokenExp ",[663,1969,1970],{"class":1390},"-",[663,1972,1496],{"class":683},[663,1974,1558],{"class":669},[663,1976,1978,1981,1983,1985,1987,1990,1992],{"class":665,"line":1977},35,[663,1979,1980],{"class":683},"      this",[663,1982,1967],{"class":669},[663,1984,1593],{"class":1390},[663,1986,1958],{"class":669},[663,1988,1989],{"class":1390},"+",[663,1991,1478],{"class":683},[663,1993,1403],{"class":669},[663,1995,1997,1999,2001,2003,2006,2009],{"class":665,"line":1996},36,[663,1998,1980],{"class":683},[663,2000,1953],{"class":669},[663,2002,1593],{"class":1390},[663,2004,2005],{"class":669}," jwt.",[663,2007,2008],{"class":1650},"sign",[663,2010,2011],{"class":669},"(\n",[663,2013,2015,2018,2020,2023,2025,2028],{"class":665,"line":2014},37,[663,2016,2017],{"class":669},"        { sub: ",[663,2019,1950],{"class":683},[663,2021,2022],{"class":669},".#apiKey, iat: now, exp: ",[663,2024,1950],{"class":683},[663,2026,2027],{"class":669},".#tokenExp }, ",[663,2029,2030],{"class":1384},"\u002F\u002F iat and exp are both required\n",[663,2032,2034,2037],{"class":665,"line":2033},38,[663,2035,2036],{"class":683},"        this",[663,2038,2039],{"class":669},".#apiSecret,\n",[663,2041,2043,2046,2049,2052,2054,2057],{"class":665,"line":2042},39,[663,2044,2045],{"class":669},"        { algorithm: ",[663,2047,2048],{"class":698},"'HS256'",[663,2050,2051],{"class":669},", keyid: ",[663,2053,1950],{"class":683},[663,2055,2056],{"class":669},".#apiKey },          ",[663,2058,2059],{"class":1384},"\u002F\u002F keyid becomes the kid header\n",[663,2061,2063],{"class":665,"line":2062},40,[663,2064,2065],{"class":669},"      );\n",[663,2067,2069],{"class":665,"line":2068},41,[663,2070,2071],{"class":669},"    }\n",[663,2073,2075,2078,2080],{"class":665,"line":2074},42,[663,2076,2077],{"class":1390},"    return",[663,2079,1964],{"class":683},[663,2081,2082],{"class":669},".#token;\n",[663,2084,2086],{"class":665,"line":2085},43,[663,2087,861],{"class":669},[663,2089,2091],{"class":665,"line":2090},44,[663,2092,1451],{"emptyLinePlaceholder":1450},[663,2094,2096,2099,2102,2105,2107,2110,2112,2115,2118,2121,2123,2125,2127,2130,2132,2135,2138,2140],{"class":665,"line":2095},45,[663,2097,2098],{"class":1390},"  async",[663,2100,2101],{"class":669}," #",[663,2103,2104],{"class":1650},"send",[663,2106,1542],{"class":669},[663,2108,2109],{"class":1365},"method",[663,2111,721],{"class":669},[663,2113,2114],{"class":1365},"path",[663,2116,2117],{"class":669},", { ",[663,2119,2120],{"class":1365},"query",[663,2122,721],{"class":669},[663,2124,1550],{"class":1365},[663,2126,721],{"class":669},[663,2128,2129],{"class":1365},"timeoutMs",[663,2131,1462],{"class":1390},[663,2133,2134],{"class":683}," 30_000",[663,2136,2137],{"class":669}," } ",[663,2139,1593],{"class":1390},[663,2141,2142],{"class":669}," {}) {\n",[663,2144,2146,2148,2151,2153,2155,2158,2160,2163,2166],{"class":665,"line":2145},46,[663,2147,1911],{"class":1390},[663,2149,2150],{"class":683}," url",[663,2152,1462],{"class":1390},[663,2154,1823],{"class":1390},[663,2156,2157],{"class":1650}," URL",[663,2159,1542],{"class":669},[663,2161,2162],{"class":683},"BASE_URL",[663,2164,2165],{"class":1390}," +",[663,2167,2168],{"class":669}," path);\n",[663,2170,2172,2175,2177,2179,2182,2185,2187,2190,2193,2196,2199,2202,2205,2207,2210,2213,2216,2219],{"class":665,"line":2171},47,[663,2173,2174],{"class":1390},"    for",[663,2176,1783],{"class":669},[663,2178,1456],{"class":1390},[663,2180,2181],{"class":669}," [",[663,2183,2184],{"class":683},"key",[663,2186,721],{"class":669},[663,2188,2189],{"class":683},"value",[663,2191,2192],{"class":669},"] ",[663,2194,2195],{"class":1390},"of",[663,2197,2198],{"class":669}," Object.",[663,2200,2201],{"class":1650},"entries",[663,2203,2204],{"class":669},"(query ",[663,2206,1570],{"class":1390},[663,2208,2209],{"class":669}," {})) url.searchParams.",[663,2211,2212],{"class":1650},"set",[663,2214,2215],{"class":669},"(key, ",[663,2217,2218],{"class":1650},"String",[663,2220,2221],{"class":669},"(value));\n",[663,2223,2225,2227,2230,2232,2235,2238],{"class":665,"line":2224},48,[663,2226,1911],{"class":1390},[663,2228,2229],{"class":683}," res",[663,2231,1462],{"class":1390},[663,2233,2234],{"class":1390}," await",[663,2236,2237],{"class":1650}," fetch",[663,2239,2240],{"class":669},"(url, {\n",[663,2242,2244],{"class":665,"line":2243},49,[663,2245,2246],{"class":669},"      method,\n",[663,2248,2250],{"class":665,"line":2249},50,[663,2251,2252],{"class":669},"      headers: {\n",[663,2254,2256,2259,2262,2264,2266,2269,2272,2274],{"class":665,"line":2255},51,[663,2257,2258],{"class":669},"        Authorization: ",[663,2260,2261],{"class":698},"`Bearer ${",[663,2263,1950],{"class":683},[663,2265,497],{"class":698},[663,2267,2268],{"class":1650},"#bearer",[663,2270,2271],{"class":698},"()",[663,2273,1578],{"class":698},[663,2275,687],{"class":669},[663,2277,2279,2282,2284,2287],{"class":665,"line":2278},52,[663,2280,2281],{"class":698},"        'X-Request-Id'",[663,2283,680],{"class":669},[663,2285,2286],{"class":1650},"randomUUID",[663,2288,2289],{"class":669},"(),\n",[663,2291,2293,2296,2299,2302,2304,2307,2310,2312,2315,2318,2320,2323],{"class":665,"line":2292},53,[663,2294,2295],{"class":1390},"        ...",[663,2297,2298],{"class":669},"(body ",[663,2300,2301],{"class":1390},"===",[663,2303,1690],{"class":683},[663,2305,2306],{"class":1390}," ?",[663,2308,2309],{"class":669}," {} ",[663,2311,419],{"class":1390},[663,2313,2314],{"class":669}," { ",[663,2316,2317],{"class":698},"'Content-Type'",[663,2319,680],{"class":669},[663,2321,2322],{"class":698},"'application\u002Fjson'",[663,2324,2325],{"class":669}," }),\n",[663,2327,2329],{"class":665,"line":2328},54,[663,2330,2331],{"class":669},"      },\n",[663,2333,2335,2338,2340,2342,2344,2346,2349,2352,2354,2357],{"class":665,"line":2334},55,[663,2336,2337],{"class":669},"      body: body ",[663,2339,2301],{"class":1390},[663,2341,1690],{"class":683},[663,2343,2306],{"class":1390},[663,2345,1690],{"class":683},[663,2347,2348],{"class":1390}," :",[663,2350,2351],{"class":683}," JSON",[663,2353,497],{"class":669},[663,2355,2356],{"class":1650},"stringify",[663,2358,2359],{"class":669},"(body),\n",[663,2361,2363,2366,2369],{"class":665,"line":2362},56,[663,2364,2365],{"class":669},"      signal: AbortSignal.",[663,2367,2368],{"class":1650},"timeout",[663,2370,2371],{"class":669},"(timeoutMs),\n",[663,2373,2375],{"class":665,"line":2374},57,[663,2376,2377],{"class":669},"    });\n",[663,2379,2381,2383,2385,2387,2390,2392,2394,2396,2399,2402,2405,2407,2410,2413,2416,2419,2421],{"class":665,"line":2380},58,[663,2382,1780],{"class":1390},[663,2384,1783],{"class":669},[663,2386,1786],{"class":1390},[663,2388,2389],{"class":669},"res.ok) ",[663,2391,1820],{"class":1390},[663,2393,1823],{"class":1390},[663,2395,1524],{"class":1650},[663,2397,2398],{"class":669},"(res.status, ",[663,2400,2401],{"class":1390},"await",[663,2403,2404],{"class":669}," res.",[663,2406,658],{"class":1650},[663,2408,2409],{"class":669},"().",[663,2411,2412],{"class":1650},"catch",[663,2414,2415],{"class":669},"(() ",[663,2417,2418],{"class":1390},"=>",[663,2420,1740],{"class":683},[663,2422,2423],{"class":669},"), res.headers);\n",[663,2425,2427,2429],{"class":665,"line":2426},59,[663,2428,2077],{"class":1390},[663,2430,2431],{"class":669}," res;\n",[663,2433,2435],{"class":665,"line":2434},60,[663,2436,861],{"class":669},[663,2438,2440],{"class":665,"line":2439},61,[663,2441,1451],{"emptyLinePlaceholder":1450},[663,2443,2445],{"class":665,"line":2444},62,[663,2446,2447],{"class":1384},"  \u002F** A JSON route. Resolves to the envelope: { success, message, data, pagination? }. *\u002F\n",[663,2449,2451,2453,2456,2458,2460,2462,2464,2466,2469],{"class":665,"line":2450},63,[663,2452,2098],{"class":1390},[663,2454,2455],{"class":1650}," request",[663,2457,1542],{"class":669},[663,2459,2109],{"class":1365},[663,2461,721],{"class":669},[663,2463,2114],{"class":1365},[663,2465,721],{"class":669},[663,2467,2468],{"class":1365},"options",[663,2470,1558],{"class":669},[663,2472,2474,2476,2479,2481,2483,2485,2487,2489,2491,2494,2497,2499],{"class":665,"line":2473},64,[663,2475,1911],{"class":1390},[663,2477,2478],{"class":683}," json",[663,2480,1462],{"class":1390},[663,2482,2234],{"class":1390},[663,2484,1783],{"class":669},[663,2486,2401],{"class":1390},[663,2488,1964],{"class":683},[663,2490,497],{"class":669},[663,2492,2493],{"class":1650},"#send",[663,2495,2496],{"class":669},"(method, path, options)).",[663,2498,658],{"class":1650},[663,2500,2501],{"class":669},"();\n",[663,2503,2505],{"class":665,"line":2504},65,[663,2506,2507],{"class":1384},"    \u002F\u002F validate-me is the one JSON route that answers without an envelope.\n",[663,2509,2511,2513,2516,2518,2521,2523,2526,2528,2531,2533],{"class":665,"line":2510},66,[663,2512,2077],{"class":1390},[663,2514,2515],{"class":669}," path ",[663,2517,2301],{"class":1390},[663,2519,2520],{"class":698}," '\u002Fapi-account\u002Fvalidate-me'",[663,2522,2306],{"class":1390},[663,2524,2525],{"class":669}," { success: ",[663,2527,684],{"class":683},[663,2529,2530],{"class":669},", data: json } ",[663,2532,419],{"class":1390},[663,2534,2535],{"class":669}," json;\n",[663,2537,2539],{"class":665,"line":2538},67,[663,2540,861],{"class":669},[663,2542,2544],{"class":665,"line":2543},68,[663,2545,1451],{"emptyLinePlaceholder":1450},[663,2547,2549],{"class":665,"line":2548},69,[663,2550,2551],{"class":1384},"  \u002F** Every item of a paginated list, 100 per page (the maximum). *\u002F\n",[663,2553,2555,2557,2560,2563,2565,2567,2569,2571,2573],{"class":665,"line":2554},70,[663,2556,2098],{"class":1390},[663,2558,2559],{"class":1390}," *",[663,2561,2562],{"class":1650},"paginate",[663,2564,1542],{"class":669},[663,2566,2114],{"class":1365},[663,2568,721],{"class":669},[663,2570,2120],{"class":1365},[663,2572,1462],{"class":1390},[663,2574,2142],{"class":669},[663,2576,2578,2580,2582,2585,2588,2590,2593,2596,2599],{"class":665,"line":2577},71,[663,2579,2174],{"class":1390},[663,2581,1783],{"class":669},[663,2583,2584],{"class":1390},"let",[663,2586,2587],{"class":669}," page ",[663,2589,1593],{"class":1390},[663,2591,2592],{"class":683}," 1",[663,2594,2595],{"class":669},"; ; page",[663,2597,2598],{"class":1390},"++",[663,2600,1558],{"class":669},[663,2602,2604,2607,2609,2612,2614,2616,2618,2620,2622,2624,2626,2629,2631,2634,2637,2640,2643,2646],{"class":665,"line":2603},72,[663,2605,2606],{"class":1390},"      const",[663,2608,2314],{"class":669},[663,2610,2611],{"class":683},"data",[663,2613,721],{"class":669},[663,2615,791],{"class":683},[663,2617,2137],{"class":669},[663,2619,1593],{"class":1390},[663,2621,2234],{"class":1390},[663,2623,1964],{"class":683},[663,2625,497],{"class":669},[663,2627,2628],{"class":1650},"request",[663,2630,1542],{"class":669},[663,2632,2633],{"class":698},"'GET'",[663,2635,2636],{"class":669},", path, { query: { ",[663,2638,2639],{"class":1390},"...",[663,2641,2642],{"class":669},"query, page, limit: ",[663,2644,2645],{"class":683},"100",[663,2647,2648],{"class":669}," } });\n",[663,2650,2652,2655],{"class":665,"line":2651},73,[663,2653,2654],{"class":1390},"      yield*",[663,2656,2657],{"class":669}," data;\n",[663,2659,2661,2664,2666,2668,2671,2673,2675,2677,2680,2683],{"class":665,"line":2660},74,[663,2662,2663],{"class":1390},"      if",[663,2665,1783],{"class":669},[663,2667,1786],{"class":1390},[663,2669,2670],{"class":669},"pagination ",[663,2672,1687],{"class":1390},[663,2674,2587],{"class":669},[663,2676,1961],{"class":1390},[663,2678,2679],{"class":669}," pagination.totalPages) ",[663,2681,2682],{"class":1390},"return",[663,2684,1403],{"class":669},[663,2686,2688],{"class":665,"line":2687},75,[663,2689,2071],{"class":669},[663,2691,2693],{"class":665,"line":2692},76,[663,2694,861],{"class":669},[663,2696,2698],{"class":665,"line":2697},77,[663,2699,1451],{"emptyLinePlaceholder":1450},[663,2701,2703],{"class":665,"line":2702},78,[663,2704,2705],{"class":1384},"  \u002F** A certificate or report download. Saves the file and resolves to its name. *\u002F\n",[663,2707,2709,2711,2714,2716,2718,2720,2723,2725,2728],{"class":665,"line":2708},79,[663,2710,2098],{"class":1390},[663,2712,2713],{"class":1650}," download",[663,2715,1542],{"class":669},[663,2717,2114],{"class":1365},[663,2719,721],{"class":669},[663,2721,2722],{"class":1365},"directory",[663,2724,1462],{"class":1390},[663,2726,2727],{"class":698}," '.'",[663,2729,1558],{"class":669},[663,2731,2733,2735,2737,2739,2741,2743,2745,2747,2749,2751,2754,2757],{"class":665,"line":2732},80,[663,2734,1911],{"class":1390},[663,2736,2229],{"class":683},[663,2738,1462],{"class":1390},[663,2740,2234],{"class":1390},[663,2742,1964],{"class":683},[663,2744,497],{"class":669},[663,2746,2493],{"class":1650},[663,2748,1542],{"class":669},[663,2750,2633],{"class":698},[663,2752,2753],{"class":669},", path, { timeoutMs: ",[663,2755,2756],{"class":683},"120_000",[663,2758,2759],{"class":669}," });\n",[663,2761,2763,2765,2768,2770,2773,2775,2777,2779,2781,2784,2787],{"class":665,"line":2762},81,[663,2764,1911],{"class":1390},[663,2766,2767],{"class":683}," bytes",[663,2769,1462],{"class":1390},[663,2771,2772],{"class":669}," Buffer.",[663,2774,1397],{"class":1650},[663,2776,1542],{"class":669},[663,2778,2401],{"class":1390},[663,2780,2404],{"class":669},[663,2782,2783],{"class":1650},"arrayBuffer",[663,2785,2786],{"class":669},"()); ",[663,2788,2789],{"class":1384},"\u002F\u002F rejects if the connection drops mid-body\n",[663,2791,2793,2795,2798,2800,2802,2805,2807,2809,2812],{"class":665,"line":2792},82,[663,2794,1911],{"class":1390},[663,2796,2797],{"class":683}," expected",[663,2799,1462],{"class":1390},[663,2801,1671],{"class":1650},[663,2803,2804],{"class":669},"(res.headers.",[663,2806,1651],{"class":1650},[663,2808,1542],{"class":669},[663,2810,2811],{"class":698},"'content-length'",[663,2813,2814],{"class":669},"));\n",[663,2816,2818,2820,2823,2825,2828,2830,2832,2834,2837,2840],{"class":665,"line":2817},83,[663,2819,1911],{"class":1390},[663,2821,2822],{"class":683}," decoded",[663,2824,1462],{"class":1390},[663,2826,2827],{"class":1650}," Boolean",[663,2829,2804],{"class":669},[663,2831,1651],{"class":1650},[663,2833,1542],{"class":669},[663,2835,2836],{"class":698},"'content-encoding'",[663,2838,2839],{"class":669},")); ",[663,2841,2842],{"class":1384},"\u002F\u002F then Content-Length is the encoded size\n",[663,2844,2846,2848,2851,2854,2857,2860,2862,2865,2868,2871,2874,2876,2878,2880,2882,2885],{"class":665,"line":2845},84,[663,2847,1780],{"class":1390},[663,2849,2850],{"class":669}," (expected ",[663,2852,2853],{"class":1390},"&&",[663,2855,2856],{"class":1390}," !",[663,2858,2859],{"class":669},"decoded ",[663,2861,2853],{"class":1390},[663,2863,2864],{"class":669}," bytes.",[663,2866,2867],{"class":683},"length",[663,2869,2870],{"class":1390}," !==",[663,2872,2873],{"class":669}," expected) ",[663,2875,1820],{"class":1390},[663,2877,1823],{"class":1390},[663,2879,1530],{"class":1650},[663,2881,1542],{"class":669},[663,2883,2884],{"class":698},"'Download ended early'",[663,2886,1581],{"class":669},[663,2888,2890,2892,2895,2897,2900,2902,2905,2907,2909,2911,2914,2916,2918,2921],{"class":665,"line":2889},85,[663,2891,1911],{"class":1390},[663,2893,2894],{"class":683}," name",[663,2896,1462],{"class":1390},[663,2898,2899],{"class":1650}," basename",[663,2901,1542],{"class":669},[663,2903,2904],{"class":1650},"fileNameOf",[663,2906,2804],{"class":669},[663,2908,1651],{"class":1650},[663,2910,1542],{"class":669},[663,2912,2913],{"class":698},"'content-disposition'",[663,2915,1684],{"class":669},[663,2917,1570],{"class":1390},[663,2919,2920],{"class":698}," 'download.pdf'",[663,2922,1581],{"class":669},[663,2924,2926,2929,2932,2934,2937],{"class":665,"line":2925},86,[663,2927,2928],{"class":1390},"    await",[663,2930,2931],{"class":1650}," writeFile",[663,2933,1542],{"class":669},[663,2935,2936],{"class":1650},"join",[663,2938,2939],{"class":669},"(directory, name), bytes);\n",[663,2941,2943,2945],{"class":665,"line":2942},87,[663,2944,2077],{"class":1390},[663,2946,2947],{"class":669}," name;\n",[663,2949,2951],{"class":665,"line":2950},88,[663,2952,861],{"class":669},[663,2954,2956],{"class":665,"line":2955},89,[663,2957,786],{"class":669},[663,2959,2961],{"class":665,"line":2960},90,[663,2962,1451],{"emptyLinePlaceholder":1450},[663,2964,2966,2969,2972,2974,2977],{"class":665,"line":2965},91,[663,2967,2968],{"class":1390},"function",[663,2970,2971],{"class":1650}," fileNameOf",[663,2973,1542],{"class":669},[663,2975,2976],{"class":1365},"header",[663,2978,1558],{"class":669},[663,2980,2982,2985,2988,2990,2993,2997,3000,3003,3005,3008,3010,3013,3016,3018,3021,3024,3026,3028],{"class":665,"line":2981},92,[663,2983,2984],{"class":1390},"  const",[663,2986,2987],{"class":683}," star",[663,2989,1462],{"class":1390},[663,2991,2992],{"class":698}," \u002Ffilename",[663,2994,2996],{"class":2995},"sJaa3","\\*",[663,2998,2999],{"class":698},"=UTF-8''(",[663,3001,3002],{"class":683},"[",[663,3004,1792],{"class":1390},[663,3006,3007],{"class":683},";]",[663,3009,1989],{"class":1390},[663,3011,3012],{"class":698},")\u002F",[663,3014,3015],{"class":1390},"i",[663,3017,497],{"class":669},[663,3019,3020],{"class":1650},"exec",[663,3022,3023],{"class":669},"(header ",[663,3025,1570],{"class":1390},[663,3027,1815],{"class":698},[663,3029,1581],{"class":669},[663,3031,3033,3036,3039,3041,3044,3047,3049],{"class":665,"line":3032},93,[663,3034,3035],{"class":1390},"  if",[663,3037,3038],{"class":669}," (star) ",[663,3040,2682],{"class":1390},[663,3042,3043],{"class":1650}," decodeURIComponent",[663,3045,3046],{"class":669},"(star[",[663,3048,749],{"class":683},[663,3050,3051],{"class":669},"]);\n",[663,3053,3055,3058,3061,3063,3065,3068,3071,3074,3076,3078,3080,3082,3084,3086,3089,3091,3093,3095,3097],{"class":665,"line":3054},94,[663,3056,3057],{"class":1390},"  return",[663,3059,3060],{"class":698}," \u002Ffilename=\"(",[663,3062,3002],{"class":683},[663,3064,1792],{"class":1390},[663,3066,3067],{"class":683},"\"]",[663,3069,3070],{"class":1390},"*",[663,3072,3073],{"class":698},")\"\u002F",[663,3075,3015],{"class":1390},[663,3077,497],{"class":669},[663,3079,3020],{"class":1650},[663,3081,3023],{"class":669},[663,3083,1570],{"class":1390},[663,3085,1815],{"class":698},[663,3087,3088],{"class":669},")?.[",[663,3090,749],{"class":683},[663,3092,2192],{"class":669},[663,3094,1570],{"class":1390},[663,3096,1740],{"class":683},[663,3098,1403],{"class":669},[663,3100,3102],{"class":665,"line":3101},95,[663,3103,786],{"class":669},[358,3105,3106],{},"Using it:",[654,3108,3110],{"className":1375,"code":3109,"language":1377,"meta":659,"style":659},"import { MainTeamClient, ApiError } from '.\u002Fmain-team-client.mjs';\n\nconst api = new MainTeamClient({\n  apiKey: process.env.MTO_API_KEY,\n  apiSecret: process.env.MTO_API_SECRET,\n});\n\n\u002F\u002F 1. Credentials work? validate-me returns the bare account.\nconst { data: me } = await api.request('GET', '\u002Fapi-account\u002Fvalidate-me');\nconsole.log(`Signed in as ${me.companyName}`);\n\n\u002F\u002F 2. Organization ids, looked up once and cached.\nconst orgIds = {};\nfor await (const org of api.paginate('\u002Forganization')) orgIds[org.slug] = org._id;\n\n\u002F\u002F 3. Walk every student on the core record.\nfor await (const student of api.paginate('\u002Fstudent')) console.log(student._id, student.username);\n\n\u002F\u002F 4. One student's released certificates on stem, downloaded.\nconst studentId = '66f2b7c1e4a9d20012ab34cd'; \u002F\u002F a core id, as registration returned it\ntry {\n  for await (const cert of api.paginate(`\u002F${orgIds.stem}\u002Fcertificate\u002F${studentId}`)) {\n    await api.download(`\u002F${orgIds.stem}\u002Fcertificate\u002Fdownload\u002F${cert._id}`, '.\u002Fexports');\n  }\n} catch (error) {\n  if (!(error instanceof ApiError)) throw error;\n  console.error(error.status, error.code, error.message, `request_id=${error.requestId}`);\n}\n",[372,3111,3112,3126,3130,3146,3156,3166,3171,3175,3180,3215,3239,3243,3248,3260,3294,3298,3303,3335,3339,3344,3361,3368,3412,3450,3454,3464,3487,3512],{"__ignoreMap":659},[663,3113,3114,3116,3119,3121,3124],{"class":665,"line":666},[663,3115,1391],{"class":1390},[663,3117,3118],{"class":669}," { MainTeamClient, ApiError } ",[663,3120,1397],{"class":1390},[663,3122,3123],{"class":698}," '.\u002Fmain-team-client.mjs'",[663,3125,1403],{"class":669},[663,3127,3128],{"class":665,"line":673},[663,3129,1451],{"emptyLinePlaceholder":1450},[663,3131,3132,3134,3137,3139,3141,3143],{"class":665,"line":690},[663,3133,1456],{"class":1390},[663,3135,3136],{"class":683}," api",[663,3138,1462],{"class":1390},[663,3140,1823],{"class":1390},[663,3142,1717],{"class":1650},[663,3144,3145],{"class":669},"({\n",[663,3147,3148,3151,3154],{"class":665,"line":704},[663,3149,3150],{"class":669},"  apiKey: process.env.",[663,3152,3153],{"class":683},"MTO_API_KEY",[663,3155,687],{"class":669},[663,3157,3158,3161,3164],{"class":665,"line":735},[663,3159,3160],{"class":669},"  apiSecret: process.env.",[663,3162,3163],{"class":683},"MTO_API_SECRET",[663,3165,687],{"class":669},[663,3167,3168],{"class":665,"line":783},[663,3169,3170],{"class":669},"});\n",[663,3172,3173],{"class":665,"line":858},[663,3174,1451],{"emptyLinePlaceholder":1450},[663,3176,3177],{"class":665,"line":864},[663,3178,3179],{"class":1384},"\u002F\u002F 1. Credentials work? validate-me returns the bare account.\n",[663,3181,3182,3184,3186,3188,3190,3193,3195,3197,3199,3202,3204,3206,3208,3210,3213],{"class":665,"line":1491},[663,3183,1456],{"class":1390},[663,3185,2314],{"class":669},[663,3187,2611],{"class":1365},[663,3189,680],{"class":669},[663,3191,3192],{"class":683},"me",[663,3194,2137],{"class":669},[663,3196,1593],{"class":1390},[663,3198,2234],{"class":1390},[663,3200,3201],{"class":669}," api.",[663,3203,2628],{"class":1650},[663,3205,1542],{"class":669},[663,3207,2633],{"class":698},[663,3209,721],{"class":669},[663,3211,3212],{"class":698},"'\u002Fapi-account\u002Fvalidate-me'",[663,3214,1581],{"class":669},[663,3216,3217,3220,3223,3225,3228,3230,3232,3235,3237],{"class":665,"line":1510},[663,3218,3219],{"class":669},"console.",[663,3221,3222],{"class":1650},"log",[663,3224,1542],{"class":669},[663,3226,3227],{"class":698},"`Signed in as ${",[663,3229,3192],{"class":669},[663,3231,497],{"class":698},[663,3233,3234],{"class":669},"companyName",[663,3236,1578],{"class":698},[663,3238,1581],{"class":669},[663,3240,3241],{"class":665,"line":1515},[663,3242,1451],{"emptyLinePlaceholder":1450},[663,3244,3245],{"class":665,"line":1536},[663,3246,3247],{"class":1384},"\u002F\u002F 2. Organization ids, looked up once and cached.\n",[663,3249,3250,3252,3255,3257],{"class":665,"line":1561},[663,3251,1456],{"class":1390},[663,3253,3254],{"class":683}," orgIds",[663,3256,1462],{"class":1390},[663,3258,3259],{"class":669}," {};\n",[663,3261,3262,3265,3267,3269,3271,3274,3277,3279,3281,3283,3286,3289,3291],{"class":665,"line":1584},[663,3263,3264],{"class":1390},"for",[663,3266,2234],{"class":1390},[663,3268,1783],{"class":669},[663,3270,1456],{"class":1390},[663,3272,3273],{"class":683}," org",[663,3275,3276],{"class":1390}," of",[663,3278,3201],{"class":669},[663,3280,2562],{"class":1650},[663,3282,1542],{"class":669},[663,3284,3285],{"class":698},"'\u002Forganization'",[663,3287,3288],{"class":669},")) orgIds[org.slug] ",[663,3290,1593],{"class":1390},[663,3292,3293],{"class":669}," org._id;\n",[663,3295,3296],{"class":665,"line":1599},[663,3297,1451],{"emptyLinePlaceholder":1450},[663,3299,3300],{"class":665,"line":1619},[663,3301,3302],{"class":1384},"\u002F\u002F 3. Walk every student on the core record.\n",[663,3304,3305,3307,3309,3311,3313,3316,3318,3320,3322,3324,3327,3330,3332],{"class":665,"line":1632},[663,3306,3264],{"class":1390},[663,3308,2234],{"class":1390},[663,3310,1783],{"class":669},[663,3312,1456],{"class":1390},[663,3314,3315],{"class":683}," student",[663,3317,3276],{"class":1390},[663,3319,3201],{"class":669},[663,3321,2562],{"class":1650},[663,3323,1542],{"class":669},[663,3325,3326],{"class":698},"'\u002Fstudent'",[663,3328,3329],{"class":669},")) console.",[663,3331,3222],{"class":1650},[663,3333,3334],{"class":669},"(student._id, student.username);\n",[663,3336,3337],{"class":665,"line":1661},[663,3338,1451],{"emptyLinePlaceholder":1450},[663,3340,3341],{"class":665,"line":1695},[663,3342,3343],{"class":1384},"\u002F\u002F 4. One student's released certificates on stem, downloaded.\n",[663,3345,3346,3348,3351,3353,3356,3358],{"class":665,"line":1700},[663,3347,1456],{"class":1390},[663,3349,3350],{"class":683}," studentId",[663,3352,1462],{"class":1390},[663,3354,3355],{"class":698}," '66f2b7c1e4a9d20012ab34cd'",[663,3357,1468],{"class":669},[663,3359,3360],{"class":1384},"\u002F\u002F a core id, as registration returned it\n",[663,3362,3363,3366],{"class":665,"line":1705},[663,3364,3365],{"class":1390},"try",[663,3367,1533],{"class":669},[663,3369,3370,3373,3375,3377,3379,3382,3384,3386,3388,3390,3393,3396,3398,3401,3404,3407,3409],{"class":665,"line":1710},[663,3371,3372],{"class":1390},"  for",[663,3374,2234],{"class":1390},[663,3376,1783],{"class":669},[663,3378,1456],{"class":1390},[663,3380,3381],{"class":683}," cert",[663,3383,3276],{"class":1390},[663,3385,3201],{"class":669},[663,3387,2562],{"class":1650},[663,3389,1542],{"class":669},[663,3391,3392],{"class":698},"`\u002F${",[663,3394,3395],{"class":669},"orgIds",[663,3397,497],{"class":698},[663,3399,3400],{"class":669},"stem",[663,3402,3403],{"class":698},"}\u002Fcertificate\u002F${",[663,3405,3406],{"class":669},"studentId",[663,3408,1578],{"class":698},[663,3410,3411],{"class":669},")) {\n",[663,3413,3414,3416,3418,3421,3423,3425,3427,3429,3431,3434,3437,3439,3441,3443,3445,3448],{"class":665,"line":1722},[663,3415,2928],{"class":1390},[663,3417,3201],{"class":669},[663,3419,3420],{"class":1650},"download",[663,3422,1542],{"class":669},[663,3424,3392],{"class":698},[663,3426,3395],{"class":669},[663,3428,497],{"class":698},[663,3430,3400],{"class":669},[663,3432,3433],{"class":698},"}\u002Fcertificate\u002Fdownload\u002F${",[663,3435,3436],{"class":669},"cert",[663,3438,497],{"class":698},[663,3440,957],{"class":669},[663,3442,1578],{"class":698},[663,3444,721],{"class":669},[663,3446,3447],{"class":698},"'.\u002Fexports'",[663,3449,1581],{"class":669},[663,3451,3452],{"class":665,"line":1755},[663,3453,861],{"class":669},[663,3455,3456,3459,3461],{"class":665,"line":1760},[663,3457,3458],{"class":669},"} ",[663,3460,2412],{"class":1390},[663,3462,3463],{"class":669}," (error) {\n",[663,3465,3466,3468,3470,3472,3475,3478,3480,3482,3484],{"class":665,"line":1777},[663,3467,3035],{"class":1390},[663,3469,1783],{"class":669},[663,3471,1786],{"class":1390},[663,3473,3474],{"class":669},"(error ",[663,3476,3477],{"class":1390},"instanceof",[663,3479,1524],{"class":1365},[663,3481,1684],{"class":669},[663,3483,1820],{"class":1390},[663,3485,3486],{"class":669}," error;\n",[663,3488,3489,3492,3495,3498,3501,3503,3505,3508,3510],{"class":665,"line":1835},[663,3490,3491],{"class":669},"  console.",[663,3493,3494],{"class":1650},"error",[663,3496,3497],{"class":669},"(error.status, error.code, error.message, ",[663,3499,3500],{"class":698},"`request_id=${",[663,3502,3494],{"class":669},[663,3504,497],{"class":698},[663,3506,3507],{"class":669},"requestId",[663,3509,1578],{"class":698},[663,3511,1581],{"class":669},[663,3513,3514],{"class":665,"line":1860},[663,3515,786],{"class":669},[404,3517,3519],{"id":3518},"a-minimal-client-in-php","A minimal client in PHP",[358,3521,3522,3523,3526],{},"About 130 lines, for PHP 8.1 or newer with ",[372,3524,3525],{},"ext-curl",". It needs one dependency:",[654,3528,3530],{"className":1356,"code":3529,"language":1358,"meta":659,"style":659},"composer require firebase\u002Fphp-jwt\n",[372,3531,3532],{"__ignoreMap":659},[663,3533,3534,3537,3540],{"class":665,"line":666},[663,3535,3536],{"class":1365},"composer",[663,3538,3539],{"class":698}," require",[663,3541,3542],{"class":698}," firebase\u002Fphp-jwt\n",[654,3544,3548],{"className":3545,"code":3546,"language":3547,"meta":659,"style":659},"language-php shiki shiki-themes github-light-high-contrast github-dark-high-contrast","\u003C?php\n\u002F\u002F MainTeamClient.php: a minimal Main Team API client (PHP 8.1+, ext-curl, firebase\u002Fphp-jwt).\ndeclare(strict_types=1);\n\nuse Firebase\\JWT\\JWT;\n\nfinal class ApiError extends RuntimeException\n{\n    public function __construct(\n        public readonly int $status,\n        public readonly string $errorCode,\n        string $message,\n        public readonly ?string $requestId = null,\n        public readonly ?int $retryAfter = null,\n    ) {\n        parent::__construct($message);\n    }\n}\n\nfinal class MainTeamClient\n{\n    private const BASE_URL = 'https:\u002F\u002Fapi.main-team.org\u002Fv1'; \u002F\u002F sandbox: 'https:\u002F\u002Fapisnd.main-team.org\u002Fv1'\n    private const TOKEN_LIFETIME = 900; \u002F\u002F seconds; the API accepts at most 3600\n    private const RENEW_BEFORE = 60;    \u002F\u002F sign a new token this long before exp\n\n    private ?string $token = null;\n    private int $tokenExp = 0;\n\n    public function __construct(private readonly string $apiKey, private readonly string $apiSecret)\n    {\n        if (!preg_match('\u002F^key_[A-Za-z0-9_-]{24}$\u002F', $apiKey)) {\n            throw new InvalidArgumentException('apiKey is not in the issued format');\n        }\n        if ($apiSecret === '') {\n            throw new InvalidArgumentException('apiSecret is missing');\n        }\n    }\n\n    private function bearer(): string\n    {\n        $now = time();\n        if ($this->token === null || $now >= $this->tokenExp - self::RENEW_BEFORE) {\n            $this->tokenExp = $now + self::TOKEN_LIFETIME;\n            $this->token = JWT::encode(\n                ['sub' => $this->apiKey, 'iat' => $now, 'exp' => $this->tokenExp],\n                $this->apiSecret,\n                'HS256',\n                $this->apiKey, \u002F\u002F the fourth argument becomes the kid header\n            );\n        }\n        return $this->token;\n    }\n\n    \u002F** One request. Returns [headers, body]; throws ApiError on a 4xx or 5xx answer. *\u002F\n    private function send(string $method, string $path, array $query = [], ?array $body = null, int $timeout = 30): array\n    {\n        $headers = [];\n        $ch = curl_init(self::BASE_URL . $path . ($query ? '?' . http_build_query($query) : ''));\n        $sent = ['Authorization: Bearer ' . $this->bearer(), 'X-Request-Id: ' . bin2hex(random_bytes(16))];\n        if ($body !== null) {\n            $sent[] = 'Content-Type: application\u002Fjson';\n            curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($body, JSON_THROW_ON_ERROR));\n        }\n        curl_setopt_array($ch, [\n            CURLOPT_CUSTOMREQUEST => $method,\n            CURLOPT_RETURNTRANSFER => true,\n            CURLOPT_CONNECTTIMEOUT => 5,\n            CURLOPT_TIMEOUT => $timeout,\n            CURLOPT_HTTPHEADER => $sent,\n            CURLOPT_HEADERFUNCTION => static function ($ch, string $line) use (&$headers): int {\n                if (str_contains($line, ':')) {\n                    [$name, $value] = explode(':', $line, 2);\n                    $headers[strtolower(trim($name))] = trim($value);\n                }\n                return strlen($line);\n            },\n        ]);\n        $raw = curl_exec($ch); \u002F\u002F false on a network error or a transfer cut short\n        if ($raw === false) {\n            throw new RuntimeException('Request failed: ' . curl_error($ch));\n        }\n        $status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);\n        if ($status >= 400) {\n            $error = json_decode($raw, true)['error'] ?? [];\n            throw new ApiError(\n                $status,\n                $error['code'] ?? 'unknown',\n                $error['message'] ?? \"HTTP $status\",\n                $error['request_id'] ?? $headers['x-request-id'] ?? null,\n                isset($headers['retry-after']) ? (int) $headers['retry-after'] : null,\n            );\n        }\n        return [$headers, $raw];\n    }\n\n    \u002F** A JSON route. Returns the envelope: success, message, data and maybe pagination. *\u002F\n    public function request(string $method, string $path, array $query = [], ?array $body = null): array\n    {\n        [, $raw] = $this->send($method, $path, $query, $body);\n        $json = json_decode($raw, true, 512, JSON_THROW_ON_ERROR);\n        \u002F\u002F validate-me is the one JSON route that answers without an envelope.\n        return $path === '\u002Fapi-account\u002Fvalidate-me' ? ['success' => true, 'data' => $json] : $json;\n    }\n\n    \u002F** Every item of a paginated list, 100 per page (the maximum). *\u002F\n    public function paginate(string $path, array $query = []): Generator\n    {\n        for ($page = 1; ; $page++) {\n            $envelope = $this->request('GET', $path, ['page' => $page, 'limit' => 100] + $query);\n            foreach ($envelope['data'] as $item) {\n                yield $item;\n            }\n            if ($page >= ($envelope['pagination']['totalPages'] ?? 0)) {\n                return;\n            }\n        }\n    }\n\n    \u002F** A certificate or report download. Saves the file and returns its name. *\u002F\n    public function download(string $path, string $directory = '.'): string\n    {\n        [$headers, $raw] = $this->send('GET', $path, timeout: 120);\n        if (isset($headers['content-length']) && strlen($raw) !== (int) $headers['content-length']) {\n            throw new RuntimeException('Download ended early');\n        }\n        $disposition = $headers['content-disposition'] ?? '';\n        $name = preg_match(\"\u002Ffilename\\\\*=UTF-8''([^;]+)\u002Fi\", $disposition, $m) ? rawurldecode($m[1])\n            : (preg_match('\u002Ffilename=\"([^\"]*)\"\u002Fi', $disposition, $m) ? $m[1] : 'download.pdf');\n        $name = basename($name);\n        file_put_contents($directory . '\u002F' . $name, $raw);\n        return $name;\n    }\n}\n","php",[372,3549,3550,3558,3563,3579,3583,3593,3597,3611,3615,3628,3642,3654,3662,3680,3698,3703,3714,3718,3722,3726,3735,3739,3757,3773,3789,3793,3808,3823,3827,3856,3861,3892,3908,3913,3926,3940,3944,3948,3952,3968,3972,3984,4029,4051,4072,4110,4120,4127,4138,4143,4147,4159,4163,4167,4172,4236,4240,4250,4298,4342,4356,4368,4392,4396,4404,4414,4426,4438,4448,4458,4494,4512,4534,4558,4563,4574,4579,4584,4600,4614,4636,4640,4657,4671,4698,4708,4713,4729,4751,4775,4807,4811,4815,4822,4826,4830,4836,4877,4882,4899,4924,4930,4967,4972,4977,4983,5013,5018,5038,5082,5101,5110,5116,5145,5152,5157,5162,5167,5172,5178,5208,5213,5243,5279,5294,5299,5319,5364,5401,5413,5432,5440,5445],{"__ignoreMap":659},[663,3551,3552,3555],{"class":665,"line":666},[663,3553,3554],{"class":1390},"\u003C?",[663,3556,3557],{"class":683},"php\n",[663,3559,3560],{"class":665,"line":673},[663,3561,3562],{"class":1384},"\u002F\u002F MainTeamClient.php: a minimal Main Team API client (PHP 8.1+, ext-curl, firebase\u002Fphp-jwt).\n",[663,3564,3565,3568,3570,3573,3575,3577],{"class":665,"line":690},[663,3566,3567],{"class":1390},"declare",[663,3569,1542],{"class":669},[663,3571,3572],{"class":683},"strict_types",[663,3574,1593],{"class":1390},[663,3576,749],{"class":683},[663,3578,1581],{"class":669},[663,3580,3581],{"class":665,"line":704},[663,3582,1451],{"emptyLinePlaceholder":1450},[663,3584,3585,3588,3591],{"class":665,"line":735},[663,3586,3587],{"class":1390},"use",[663,3589,3590],{"class":683}," Firebase\\JWT\\JWT",[663,3592,1403],{"class":669},[663,3594,3595],{"class":665,"line":783},[663,3596,1451],{"emptyLinePlaceholder":1450},[663,3598,3599,3602,3604,3606,3608],{"class":665,"line":858},[663,3600,3601],{"class":1390},"final",[663,3603,1521],{"class":1390},[663,3605,1524],{"class":1365},[663,3607,1527],{"class":1390},[663,3609,3610],{"class":683}," RuntimeException\n",[663,3612,3613],{"class":665,"line":864},[663,3614,670],{"class":669},[663,3616,3617,3620,3623,3626],{"class":665,"line":1491},[663,3618,3619],{"class":1390},"    public",[663,3621,3622],{"class":1390}," function",[663,3624,3625],{"class":683}," __construct",[663,3627,2011],{"class":669},[663,3629,3630,3633,3636,3639],{"class":665,"line":1510},[663,3631,3632],{"class":1390},"        public",[663,3634,3635],{"class":1390}," readonly",[663,3637,3638],{"class":1390}," int",[663,3640,3641],{"class":669}," $status,\n",[663,3643,3644,3646,3648,3651],{"class":665,"line":1515},[663,3645,3632],{"class":1390},[663,3647,3635],{"class":1390},[663,3649,3650],{"class":1390}," string",[663,3652,3653],{"class":669}," $errorCode,\n",[663,3655,3656,3659],{"class":665,"line":1536},[663,3657,3658],{"class":1390},"        string",[663,3660,3661],{"class":669}," $message,\n",[663,3663,3664,3666,3668,3671,3674,3676,3678],{"class":665,"line":1561},[663,3665,3632],{"class":1390},[663,3667,3635],{"class":1390},[663,3669,3670],{"class":1390}," ?string",[663,3672,3673],{"class":669}," $requestId ",[663,3675,1593],{"class":1390},[663,3677,1740],{"class":683},[663,3679,687],{"class":669},[663,3681,3682,3684,3686,3689,3692,3694,3696],{"class":665,"line":1584},[663,3683,3632],{"class":1390},[663,3685,3635],{"class":1390},[663,3687,3688],{"class":1390}," ?int",[663,3690,3691],{"class":669}," $retryAfter ",[663,3693,1593],{"class":1390},[663,3695,1740],{"class":683},[663,3697,687],{"class":669},[663,3699,3700],{"class":665,"line":1599},[663,3701,3702],{"class":669},"    ) {\n",[663,3704,3705,3708,3711],{"class":665,"line":1619},[663,3706,3707],{"class":1390},"        parent::",[663,3709,3710],{"class":1650},"__construct",[663,3712,3713],{"class":669},"($message);\n",[663,3715,3716],{"class":665,"line":1632},[663,3717,2071],{"class":669},[663,3719,3720],{"class":665,"line":1661},[663,3721,786],{"class":669},[663,3723,3724],{"class":665,"line":1695},[663,3725,1451],{"emptyLinePlaceholder":1450},[663,3727,3728,3730,3732],{"class":665,"line":1700},[663,3729,3601],{"class":1390},[663,3731,1521],{"class":1390},[663,3733,3734],{"class":1365}," MainTeamClient\n",[663,3736,3737],{"class":665,"line":1705},[663,3738,670],{"class":669},[663,3740,3741,3744,3747,3749,3751,3753,3755],{"class":665,"line":1710},[663,3742,3743],{"class":1390},"    private",[663,3745,3746],{"class":1390}," const",[663,3748,1459],{"class":683},[663,3750,1462],{"class":1390},[663,3752,1465],{"class":698},[663,3754,1468],{"class":669},[663,3756,1471],{"class":1384},[663,3758,3759,3761,3763,3765,3767,3769,3771],{"class":665,"line":1722},[663,3760,3743],{"class":1390},[663,3762,3746],{"class":1390},[663,3764,1478],{"class":683},[663,3766,1462],{"class":1390},[663,3768,1483],{"class":683},[663,3770,1468],{"class":669},[663,3772,1488],{"class":1384},[663,3774,3775,3777,3779,3781,3783,3785,3787],{"class":665,"line":1755},[663,3776,3743],{"class":1390},[663,3778,3746],{"class":1390},[663,3780,1496],{"class":683},[663,3782,1462],{"class":1390},[663,3784,1501],{"class":683},[663,3786,1504],{"class":669},[663,3788,1507],{"class":1384},[663,3790,3791],{"class":665,"line":1760},[663,3792,1451],{"emptyLinePlaceholder":1450},[663,3794,3795,3797,3799,3802,3804,3806],{"class":665,"line":1777},[663,3796,3743],{"class":1390},[663,3798,3670],{"class":1390},[663,3800,3801],{"class":669}," $token ",[663,3803,1593],{"class":1390},[663,3805,1740],{"class":683},[663,3807,1403],{"class":669},[663,3809,3810,3812,3814,3817,3819,3821],{"class":665,"line":1835},[663,3811,3743],{"class":1390},[663,3813,3638],{"class":1390},[663,3815,3816],{"class":669}," $tokenExp ",[663,3818,1593],{"class":1390},[663,3820,1750],{"class":683},[663,3822,1403],{"class":669},[663,3824,3825],{"class":665,"line":1860},[663,3826,1451],{"emptyLinePlaceholder":1450},[663,3828,3829,3831,3833,3835,3837,3840,3842,3844,3847,3849,3851,3853],{"class":665,"line":1873},[663,3830,3619],{"class":1390},[663,3832,3622],{"class":1390},[663,3834,3625],{"class":683},[663,3836,1542],{"class":669},[663,3838,3839],{"class":1390},"private",[663,3841,3635],{"class":1390},[663,3843,3650],{"class":1390},[663,3845,3846],{"class":669}," $apiKey, ",[663,3848,3839],{"class":1390},[663,3850,3635],{"class":1390},[663,3852,3650],{"class":1390},[663,3854,3855],{"class":669}," $apiSecret)\n",[663,3857,3858],{"class":665,"line":1886},[663,3859,3860],{"class":669},"    {\n",[663,3862,3863,3866,3868,3870,3873,3875,3878,3880,3883,3886,3889],{"class":665,"line":1891},[663,3864,3865],{"class":1390},"        if",[663,3867,1783],{"class":669},[663,3869,1786],{"class":1390},[663,3871,3872],{"class":683},"preg_match",[663,3874,1542],{"class":669},[663,3876,3877],{"class":698},"'\u002F",[663,3879,1792],{"class":1390},[663,3881,3882],{"class":698},"key_[A-Za-z0-9_-]{24}",[663,3884,3885],{"class":1390},"$",[663,3887,3888],{"class":698},"\u002F'",[663,3890,3891],{"class":669},", $apiKey)) {\n",[663,3893,3894,3897,3899,3902,3904,3906],{"class":665,"line":1896},[663,3895,3896],{"class":1390},"            throw",[663,3898,1823],{"class":1390},[663,3900,3901],{"class":683}," InvalidArgumentException",[663,3903,1542],{"class":669},[663,3905,1830],{"class":698},[663,3907,1581],{"class":669},[663,3909,3910],{"class":665,"line":1908},[663,3911,3912],{"class":669},"        }\n",[663,3914,3915,3917,3920,3922,3924],{"class":665,"line":1941},[663,3916,3865],{"class":1390},[663,3918,3919],{"class":669}," ($apiSecret ",[663,3921,2301],{"class":1390},[663,3923,1815],{"class":698},[663,3925,1558],{"class":669},[663,3927,3928,3930,3932,3934,3936,3938],{"class":665,"line":1977},[663,3929,3896],{"class":1390},[663,3931,1823],{"class":1390},[663,3933,3901],{"class":683},[663,3935,1542],{"class":669},[663,3937,1855],{"class":698},[663,3939,1581],{"class":669},[663,3941,3942],{"class":665,"line":1996},[663,3943,3912],{"class":669},[663,3945,3946],{"class":665,"line":2014},[663,3947,2071],{"class":669},[663,3949,3950],{"class":665,"line":2033},[663,3951,1451],{"emptyLinePlaceholder":1450},[663,3953,3954,3956,3958,3961,3963,3965],{"class":665,"line":2042},[663,3955,3743],{"class":1390},[663,3957,3622],{"class":1390},[663,3959,3960],{"class":1650}," bearer",[663,3962,2271],{"class":669},[663,3964,419],{"class":1390},[663,3966,3967],{"class":1390}," string\n",[663,3969,3970],{"class":665,"line":2062},[663,3971,3860],{"class":669},[663,3973,3974,3977,3979,3982],{"class":665,"line":2068},[663,3975,3976],{"class":669},"        $now ",[663,3978,1593],{"class":1390},[663,3980,3981],{"class":683}," time",[663,3983,2501],{"class":669},[663,3985,3986,3988,3990,3993,3996,3999,4001,4003,4006,4009,4011,4014,4016,4019,4021,4024,4027],{"class":665,"line":2074},[663,3987,3865],{"class":1390},[663,3989,1783],{"class":669},[663,3991,3992],{"class":683},"$this",[663,3994,3995],{"class":1390},"->",[663,3997,3998],{"class":669},"token ",[663,4000,2301],{"class":1390},[663,4002,1740],{"class":683},[663,4004,4005],{"class":1390}," ||",[663,4007,4008],{"class":669}," $now ",[663,4010,1961],{"class":1390},[663,4012,4013],{"class":683}," $this",[663,4015,3995],{"class":1390},[663,4017,4018],{"class":669},"tokenExp ",[663,4020,1970],{"class":1390},[663,4022,4023],{"class":1390}," self::",[663,4025,4026],{"class":683},"RENEW_BEFORE",[663,4028,1558],{"class":669},[663,4030,4031,4034,4036,4038,4040,4042,4044,4046,4049],{"class":665,"line":2085},[663,4032,4033],{"class":683},"            $this",[663,4035,3995],{"class":1390},[663,4037,4018],{"class":669},[663,4039,1593],{"class":1390},[663,4041,4008],{"class":669},[663,4043,1989],{"class":1390},[663,4045,4023],{"class":1390},[663,4047,4048],{"class":683},"TOKEN_LIFETIME",[663,4050,1403],{"class":669},[663,4052,4053,4055,4057,4059,4061,4064,4067,4070],{"class":665,"line":2090},[663,4054,4033],{"class":683},[663,4056,3995],{"class":1390},[663,4058,3998],{"class":669},[663,4060,1593],{"class":1390},[663,4062,4063],{"class":683}," JWT",[663,4065,4066],{"class":1390},"::",[663,4068,4069],{"class":1650},"encode",[663,4071,2011],{"class":669},[663,4073,4074,4077,4080,4083,4085,4087,4090,4093,4095,4098,4101,4103,4105,4107],{"class":665,"line":2095},[663,4075,4076],{"class":669},"                [",[663,4078,4079],{"class":698},"'sub'",[663,4081,4082],{"class":1390}," =>",[663,4084,4013],{"class":683},[663,4086,3995],{"class":1390},[663,4088,4089],{"class":669},"apiKey, ",[663,4091,4092],{"class":698},"'iat'",[663,4094,4082],{"class":1390},[663,4096,4097],{"class":669}," $now, ",[663,4099,4100],{"class":698},"'exp'",[663,4102,4082],{"class":1390},[663,4104,4013],{"class":683},[663,4106,3995],{"class":1390},[663,4108,4109],{"class":669},"tokenExp],\n",[663,4111,4112,4115,4117],{"class":665,"line":2145},[663,4113,4114],{"class":683},"                $this",[663,4116,3995],{"class":1390},[663,4118,4119],{"class":669},"apiSecret,\n",[663,4121,4122,4125],{"class":665,"line":2171},[663,4123,4124],{"class":698},"                'HS256'",[663,4126,687],{"class":669},[663,4128,4129,4131,4133,4135],{"class":665,"line":2224},[663,4130,4114],{"class":683},[663,4132,3995],{"class":1390},[663,4134,4089],{"class":669},[663,4136,4137],{"class":1384},"\u002F\u002F the fourth argument becomes the kid header\n",[663,4139,4140],{"class":665,"line":2243},[663,4141,4142],{"class":669},"            );\n",[663,4144,4145],{"class":665,"line":2249},[663,4146,3912],{"class":669},[663,4148,4149,4152,4154,4156],{"class":665,"line":2255},[663,4150,4151],{"class":1390},"        return",[663,4153,4013],{"class":683},[663,4155,3995],{"class":1390},[663,4157,4158],{"class":669},"token;\n",[663,4160,4161],{"class":665,"line":2278},[663,4162,2071],{"class":669},[663,4164,4165],{"class":665,"line":2292},[663,4166,1451],{"emptyLinePlaceholder":1450},[663,4168,4169],{"class":665,"line":2328},[663,4170,4171],{"class":1384},"    \u002F** One request. Returns [headers, body]; throws ApiError on a 4xx or 5xx answer. *\u002F\n",[663,4173,4174,4176,4178,4181,4183,4186,4189,4191,4194,4197,4200,4202,4205,4208,4211,4213,4215,4217,4220,4223,4225,4228,4231,4233],{"class":665,"line":2334},[663,4175,3743],{"class":1390},[663,4177,3622],{"class":1390},[663,4179,4180],{"class":1650}," send",[663,4182,1542],{"class":669},[663,4184,4185],{"class":1390},"string",[663,4187,4188],{"class":669}," $method, ",[663,4190,4185],{"class":1390},[663,4192,4193],{"class":669}," $path, ",[663,4195,4196],{"class":1390},"array",[663,4198,4199],{"class":669}," $query ",[663,4201,1593],{"class":1390},[663,4203,4204],{"class":669}," [], ",[663,4206,4207],{"class":1390},"?array",[663,4209,4210],{"class":669}," $body ",[663,4212,1593],{"class":1390},[663,4214,1740],{"class":683},[663,4216,721],{"class":669},[663,4218,4219],{"class":1390},"int",[663,4221,4222],{"class":669}," $timeout ",[663,4224,1593],{"class":1390},[663,4226,4227],{"class":683}," 30",[663,4229,4230],{"class":669},")",[663,4232,419],{"class":1390},[663,4234,4235],{"class":1390}," array\n",[663,4237,4238],{"class":665,"line":2362},[663,4239,3860],{"class":669},[663,4241,4242,4245,4247],{"class":665,"line":2374},[663,4243,4244],{"class":669},"        $headers ",[663,4246,1593],{"class":1390},[663,4248,4249],{"class":669}," [];\n",[663,4251,4252,4255,4257,4260,4262,4265,4267,4270,4273,4275,4278,4281,4284,4286,4289,4292,4294,4296],{"class":665,"line":2380},[663,4253,4254],{"class":669},"        $ch ",[663,4256,1593],{"class":1390},[663,4258,4259],{"class":683}," curl_init",[663,4261,1542],{"class":669},[663,4263,4264],{"class":1390},"self::",[663,4266,2162],{"class":683},[663,4268,4269],{"class":1390}," .",[663,4271,4272],{"class":669}," $path ",[663,4274,497],{"class":1390},[663,4276,4277],{"class":669}," ($query ",[663,4279,4280],{"class":1390},"?",[663,4282,4283],{"class":698}," '?'",[663,4285,4269],{"class":1390},[663,4287,4288],{"class":683}," http_build_query",[663,4290,4291],{"class":669},"($query) ",[663,4293,419],{"class":1390},[663,4295,1815],{"class":698},[663,4297,2814],{"class":669},[663,4299,4300,4303,4305,4307,4310,4312,4314,4316,4318,4321,4324,4326,4329,4331,4334,4336,4339],{"class":665,"line":2426},[663,4301,4302],{"class":669},"        $sent ",[663,4304,1593],{"class":1390},[663,4306,2181],{"class":669},[663,4308,4309],{"class":698},"'Authorization: Bearer '",[663,4311,4269],{"class":1390},[663,4313,4013],{"class":683},[663,4315,3995],{"class":1390},[663,4317,1902],{"class":1650},[663,4319,4320],{"class":669},"(), ",[663,4322,4323],{"class":698},"'X-Request-Id: '",[663,4325,4269],{"class":1390},[663,4327,4328],{"class":683}," bin2hex",[663,4330,1542],{"class":669},[663,4332,4333],{"class":1650},"random_bytes",[663,4335,1542],{"class":669},[663,4337,4338],{"class":683},"16",[663,4340,4341],{"class":669},"))];\n",[663,4343,4344,4346,4349,4352,4354],{"class":665,"line":2434},[663,4345,3865],{"class":1390},[663,4347,4348],{"class":669}," ($body ",[663,4350,4351],{"class":1390},"!==",[663,4353,1740],{"class":683},[663,4355,1558],{"class":669},[663,4357,4358,4361,4363,4366],{"class":665,"line":2439},[663,4359,4360],{"class":669},"            $sent[] ",[663,4362,1593],{"class":1390},[663,4364,4365],{"class":698}," 'Content-Type: application\u002Fjson'",[663,4367,1403],{"class":669},[663,4369,4370,4373,4376,4379,4381,4384,4387,4390],{"class":665,"line":2444},[663,4371,4372],{"class":683},"            curl_setopt",[663,4374,4375],{"class":669},"($ch, ",[663,4377,4378],{"class":683},"CURLOPT_POSTFIELDS",[663,4380,721],{"class":669},[663,4382,4383],{"class":683},"json_encode",[663,4385,4386],{"class":669},"($body, ",[663,4388,4389],{"class":683},"JSON_THROW_ON_ERROR",[663,4391,2814],{"class":669},[663,4393,4394],{"class":665,"line":2450},[663,4395,3912],{"class":669},[663,4397,4398,4401],{"class":665,"line":2473},[663,4399,4400],{"class":683},"        curl_setopt_array",[663,4402,4403],{"class":669},"($ch, [\n",[663,4405,4406,4409,4411],{"class":665,"line":2504},[663,4407,4408],{"class":683},"            CURLOPT_CUSTOMREQUEST",[663,4410,4082],{"class":1390},[663,4412,4413],{"class":669}," $method,\n",[663,4415,4416,4419,4421,4424],{"class":665,"line":2510},[663,4417,4418],{"class":683},"            CURLOPT_RETURNTRANSFER",[663,4420,4082],{"class":1390},[663,4422,4423],{"class":683}," true",[663,4425,687],{"class":669},[663,4427,4428,4431,4433,4436],{"class":665,"line":2538},[663,4429,4430],{"class":683},"            CURLOPT_CONNECTTIMEOUT",[663,4432,4082],{"class":1390},[663,4434,4435],{"class":683}," 5",[663,4437,687],{"class":669},[663,4439,4440,4443,4445],{"class":665,"line":2543},[663,4441,4442],{"class":683},"            CURLOPT_TIMEOUT",[663,4444,4082],{"class":1390},[663,4446,4447],{"class":669}," $timeout,\n",[663,4449,4450,4453,4455],{"class":665,"line":2548},[663,4451,4452],{"class":683},"            CURLOPT_HTTPHEADER",[663,4454,4082],{"class":1390},[663,4456,4457],{"class":669}," $sent,\n",[663,4459,4460,4463,4465,4468,4470,4473,4475,4478,4480,4482,4485,4488,4490,4492],{"class":665,"line":2554},[663,4461,4462],{"class":683},"            CURLOPT_HEADERFUNCTION",[663,4464,4082],{"class":1390},[663,4466,4467],{"class":1390}," static",[663,4469,3622],{"class":1390},[663,4471,4472],{"class":669}," ($ch, ",[663,4474,4185],{"class":1390},[663,4476,4477],{"class":669}," $line) ",[663,4479,3587],{"class":1390},[663,4481,1783],{"class":669},[663,4483,4484],{"class":1390},"&",[663,4486,4487],{"class":669},"$headers)",[663,4489,419],{"class":1390},[663,4491,3638],{"class":1390},[663,4493,1533],{"class":669},[663,4495,4496,4499,4501,4504,4507,4510],{"class":665,"line":2577},[663,4497,4498],{"class":1390},"                if",[663,4500,1783],{"class":669},[663,4502,4503],{"class":683},"str_contains",[663,4505,4506],{"class":669},"($line, ",[663,4508,4509],{"class":698},"':'",[663,4511,3411],{"class":669},[663,4513,4514,4517,4519,4522,4524,4526,4529,4532],{"class":665,"line":2603},[663,4515,4516],{"class":669},"                    [$name, $value] ",[663,4518,1593],{"class":1390},[663,4520,4521],{"class":683}," explode",[663,4523,1542],{"class":669},[663,4525,4509],{"class":698},[663,4527,4528],{"class":669},", $line, ",[663,4530,4531],{"class":683},"2",[663,4533,1581],{"class":669},[663,4535,4536,4539,4542,4544,4547,4550,4552,4555],{"class":665,"line":2651},[663,4537,4538],{"class":669},"                    $headers[",[663,4540,4541],{"class":683},"strtolower",[663,4543,1542],{"class":669},[663,4545,4546],{"class":683},"trim",[663,4548,4549],{"class":669},"($name))] ",[663,4551,1593],{"class":1390},[663,4553,4554],{"class":683}," trim",[663,4556,4557],{"class":669},"($value);\n",[663,4559,4560],{"class":665,"line":2660},[663,4561,4562],{"class":669},"                }\n",[663,4564,4565,4568,4571],{"class":665,"line":2687},[663,4566,4567],{"class":1390},"                return",[663,4569,4570],{"class":683}," strlen",[663,4572,4573],{"class":669},"($line);\n",[663,4575,4576],{"class":665,"line":2692},[663,4577,4578],{"class":669},"            },\n",[663,4580,4581],{"class":665,"line":2697},[663,4582,4583],{"class":669},"        ]);\n",[663,4585,4586,4589,4591,4594,4597],{"class":665,"line":2702},[663,4587,4588],{"class":669},"        $raw ",[663,4590,1593],{"class":1390},[663,4592,4593],{"class":683}," curl_exec",[663,4595,4596],{"class":669},"($ch); ",[663,4598,4599],{"class":1384},"\u002F\u002F false on a network error or a transfer cut short\n",[663,4601,4602,4604,4607,4609,4612],{"class":665,"line":2708},[663,4603,3865],{"class":1390},[663,4605,4606],{"class":669}," ($raw ",[663,4608,2301],{"class":1390},[663,4610,4611],{"class":683}," false",[663,4613,1558],{"class":669},[663,4615,4616,4618,4620,4623,4625,4628,4630,4633],{"class":665,"line":2732},[663,4617,3896],{"class":1390},[663,4619,1823],{"class":1390},[663,4621,4622],{"class":683}," RuntimeException",[663,4624,1542],{"class":669},[663,4626,4627],{"class":698},"'Request failed: '",[663,4629,4269],{"class":1390},[663,4631,4632],{"class":683}," curl_error",[663,4634,4635],{"class":669},"($ch));\n",[663,4637,4638],{"class":665,"line":2762},[663,4639,3912],{"class":669},[663,4641,4642,4645,4647,4650,4652,4655],{"class":665,"line":2792},[663,4643,4644],{"class":669},"        $status ",[663,4646,1593],{"class":1390},[663,4648,4649],{"class":683}," curl_getinfo",[663,4651,4375],{"class":669},[663,4653,4654],{"class":683},"CURLINFO_RESPONSE_CODE",[663,4656,1581],{"class":669},[663,4658,4659,4661,4664,4666,4669],{"class":665,"line":2817},[663,4660,3865],{"class":1390},[663,4662,4663],{"class":669}," ($status ",[663,4665,1961],{"class":1390},[663,4667,4668],{"class":683}," 400",[663,4670,1558],{"class":669},[663,4672,4673,4676,4678,4681,4684,4686,4689,4692,4694,4696],{"class":665,"line":2845},[663,4674,4675],{"class":669},"            $error ",[663,4677,1593],{"class":1390},[663,4679,4680],{"class":683}," json_decode",[663,4682,4683],{"class":669},"($raw, ",[663,4685,684],{"class":683},[663,4687,4688],{"class":669},")[",[663,4690,4691],{"class":698},"'error'",[663,4693,2192],{"class":669},[663,4695,1570],{"class":1390},[663,4697,4249],{"class":669},[663,4699,4700,4702,4704,4706],{"class":665,"line":2889},[663,4701,3896],{"class":1390},[663,4703,1823],{"class":1390},[663,4705,1524],{"class":683},[663,4707,2011],{"class":669},[663,4709,4710],{"class":665,"line":2925},[663,4711,4712],{"class":669},"                $status,\n",[663,4714,4715,4718,4721,4723,4725,4727],{"class":665,"line":2942},[663,4716,4717],{"class":669},"                $error[",[663,4719,4720],{"class":698},"'code'",[663,4722,2192],{"class":669},[663,4724,1570],{"class":1390},[663,4726,1614],{"class":698},[663,4728,687],{"class":669},[663,4730,4731,4733,4736,4738,4740,4743,4746,4749],{"class":665,"line":2950},[663,4732,4717],{"class":669},[663,4734,4735],{"class":698},"'message'",[663,4737,2192],{"class":669},[663,4739,1570],{"class":1390},[663,4741,4742],{"class":698}," \"HTTP ",[663,4744,4745],{"class":669},"$status",[663,4747,4748],{"class":698},"\"",[663,4750,687],{"class":669},[663,4752,4753,4755,4758,4760,4762,4765,4767,4769,4771,4773],{"class":665,"line":2955},[663,4754,4717],{"class":669},[663,4756,4757],{"class":698},"'request_id'",[663,4759,2192],{"class":669},[663,4761,1570],{"class":1390},[663,4763,4764],{"class":669}," $headers[",[663,4766,1656],{"class":698},[663,4768,2192],{"class":669},[663,4770,1570],{"class":1390},[663,4772,1740],{"class":683},[663,4774,687],{"class":669},[663,4776,4777,4780,4783,4785,4788,4790,4792,4794,4797,4799,4801,4803,4805],{"class":665,"line":2960},[663,4778,4779],{"class":683},"                isset",[663,4781,4782],{"class":669},"($headers[",[663,4784,1681],{"class":698},[663,4786,4787],{"class":669},"]) ",[663,4789,4280],{"class":1390},[663,4791,1783],{"class":669},[663,4793,4219],{"class":1390},[663,4795,4796],{"class":669},") $headers[",[663,4798,1681],{"class":698},[663,4800,2192],{"class":669},[663,4802,419],{"class":1390},[663,4804,1740],{"class":683},[663,4806,687],{"class":669},[663,4808,4809],{"class":665,"line":2965},[663,4810,4142],{"class":669},[663,4812,4813],{"class":665,"line":2981},[663,4814,3912],{"class":669},[663,4816,4817,4819],{"class":665,"line":3032},[663,4818,4151],{"class":1390},[663,4820,4821],{"class":669}," [$headers, $raw];\n",[663,4823,4824],{"class":665,"line":3054},[663,4825,2071],{"class":669},[663,4827,4828],{"class":665,"line":3101},[663,4829,1451],{"emptyLinePlaceholder":1450},[663,4831,4833],{"class":665,"line":4832},96,[663,4834,4835],{"class":1384},"    \u002F** A JSON route. Returns the envelope: success, message, data and maybe pagination. *\u002F\n",[663,4837,4839,4841,4843,4845,4847,4849,4851,4853,4855,4857,4859,4861,4863,4865,4867,4869,4871,4873,4875],{"class":665,"line":4838},97,[663,4840,3619],{"class":1390},[663,4842,3622],{"class":1390},[663,4844,2455],{"class":1650},[663,4846,1542],{"class":669},[663,4848,4185],{"class":1390},[663,4850,4188],{"class":669},[663,4852,4185],{"class":1390},[663,4854,4193],{"class":669},[663,4856,4196],{"class":1390},[663,4858,4199],{"class":669},[663,4860,1593],{"class":1390},[663,4862,4204],{"class":669},[663,4864,4207],{"class":1390},[663,4866,4210],{"class":669},[663,4868,1593],{"class":1390},[663,4870,1740],{"class":683},[663,4872,4230],{"class":669},[663,4874,419],{"class":1390},[663,4876,4235],{"class":1390},[663,4878,4880],{"class":665,"line":4879},98,[663,4881,3860],{"class":669},[663,4883,4885,4888,4890,4892,4894,4896],{"class":665,"line":4884},99,[663,4886,4887],{"class":669},"        [, $raw] ",[663,4889,1593],{"class":1390},[663,4891,4013],{"class":683},[663,4893,3995],{"class":1390},[663,4895,2104],{"class":1650},[663,4897,4898],{"class":669},"($method, $path, $query, $body);\n",[663,4900,4902,4905,4907,4909,4911,4913,4915,4918,4920,4922],{"class":665,"line":4901},100,[663,4903,4904],{"class":669},"        $json ",[663,4906,1593],{"class":1390},[663,4908,4680],{"class":683},[663,4910,4683],{"class":669},[663,4912,684],{"class":683},[663,4914,721],{"class":669},[663,4916,4917],{"class":683},"512",[663,4919,721],{"class":669},[663,4921,4389],{"class":683},[663,4923,1581],{"class":669},[663,4925,4927],{"class":665,"line":4926},101,[663,4928,4929],{"class":1384},"        \u002F\u002F validate-me is the one JSON route that answers without an envelope.\n",[663,4931,4933,4935,4937,4939,4941,4943,4945,4948,4950,4952,4954,4957,4959,4962,4964],{"class":665,"line":4932},102,[663,4934,4151],{"class":1390},[663,4936,4272],{"class":669},[663,4938,2301],{"class":1390},[663,4940,2520],{"class":698},[663,4942,2306],{"class":1390},[663,4944,2181],{"class":669},[663,4946,4947],{"class":698},"'success'",[663,4949,4082],{"class":1390},[663,4951,4423],{"class":683},[663,4953,721],{"class":669},[663,4955,4956],{"class":698},"'data'",[663,4958,4082],{"class":1390},[663,4960,4961],{"class":669}," $json] ",[663,4963,419],{"class":1390},[663,4965,4966],{"class":669}," $json;\n",[663,4968,4970],{"class":665,"line":4969},103,[663,4971,2071],{"class":669},[663,4973,4975],{"class":665,"line":4974},104,[663,4976,1451],{"emptyLinePlaceholder":1450},[663,4978,4980],{"class":665,"line":4979},105,[663,4981,4982],{"class":1384},"    \u002F** Every item of a paginated list, 100 per page (the maximum). *\u002F\n",[663,4984,4986,4988,4990,4993,4995,4997,4999,5001,5003,5005,5008,5010],{"class":665,"line":4985},106,[663,4987,3619],{"class":1390},[663,4989,3622],{"class":1390},[663,4991,4992],{"class":1650}," paginate",[663,4994,1542],{"class":669},[663,4996,4185],{"class":1390},[663,4998,4193],{"class":669},[663,5000,4196],{"class":1390},[663,5002,4199],{"class":669},[663,5004,1593],{"class":1390},[663,5006,5007],{"class":669}," [])",[663,5009,419],{"class":1390},[663,5011,5012],{"class":683}," Generator\n",[663,5014,5016],{"class":665,"line":5015},107,[663,5017,3860],{"class":669},[663,5019,5021,5024,5027,5029,5031,5034,5036],{"class":665,"line":5020},108,[663,5022,5023],{"class":1390},"        for",[663,5025,5026],{"class":669}," ($page ",[663,5028,1593],{"class":1390},[663,5030,2592],{"class":683},[663,5032,5033],{"class":669},"; ; $page",[663,5035,2598],{"class":1390},[663,5037,1558],{"class":669},[663,5039,5041,5044,5046,5048,5050,5052,5054,5056,5059,5062,5064,5067,5070,5072,5075,5077,5079],{"class":665,"line":5040},109,[663,5042,5043],{"class":669},"            $envelope ",[663,5045,1593],{"class":1390},[663,5047,4013],{"class":683},[663,5049,3995],{"class":1390},[663,5051,2628],{"class":1650},[663,5053,1542],{"class":669},[663,5055,2633],{"class":698},[663,5057,5058],{"class":669},", $path, [",[663,5060,5061],{"class":698},"'page'",[663,5063,4082],{"class":1390},[663,5065,5066],{"class":669}," $page, ",[663,5068,5069],{"class":698},"'limit'",[663,5071,4082],{"class":1390},[663,5073,5074],{"class":683}," 100",[663,5076,2192],{"class":669},[663,5078,1989],{"class":1390},[663,5080,5081],{"class":669}," $query);\n",[663,5083,5085,5088,5091,5093,5095,5098],{"class":665,"line":5084},110,[663,5086,5087],{"class":1390},"            foreach",[663,5089,5090],{"class":669}," ($envelope[",[663,5092,4956],{"class":698},[663,5094,2192],{"class":669},[663,5096,5097],{"class":1390},"as",[663,5099,5100],{"class":669}," $item) {\n",[663,5102,5104,5107],{"class":665,"line":5103},111,[663,5105,5106],{"class":1390},"                yield",[663,5108,5109],{"class":669}," $item;\n",[663,5111,5113],{"class":665,"line":5112},112,[663,5114,5115],{"class":669},"            }\n",[663,5117,5119,5122,5124,5126,5128,5131,5134,5137,5139,5141,5143],{"class":665,"line":5118},113,[663,5120,5121],{"class":1390},"            if",[663,5123,5026],{"class":669},[663,5125,1961],{"class":1390},[663,5127,5090],{"class":669},[663,5129,5130],{"class":698},"'pagination'",[663,5132,5133],{"class":669},"][",[663,5135,5136],{"class":698},"'totalPages'",[663,5138,2192],{"class":669},[663,5140,1570],{"class":1390},[663,5142,1750],{"class":683},[663,5144,3411],{"class":669},[663,5146,5148,5150],{"class":665,"line":5147},114,[663,5149,4567],{"class":1390},[663,5151,1403],{"class":669},[663,5153,5155],{"class":665,"line":5154},115,[663,5156,5115],{"class":669},[663,5158,5160],{"class":665,"line":5159},116,[663,5161,3912],{"class":669},[663,5163,5165],{"class":665,"line":5164},117,[663,5166,2071],{"class":669},[663,5168,5170],{"class":665,"line":5169},118,[663,5171,1451],{"emptyLinePlaceholder":1450},[663,5173,5175],{"class":665,"line":5174},119,[663,5176,5177],{"class":1384},"    \u002F** A certificate or report download. Saves the file and returns its name. *\u002F\n",[663,5179,5181,5183,5185,5187,5189,5191,5193,5195,5198,5200,5202,5204,5206],{"class":665,"line":5180},120,[663,5182,3619],{"class":1390},[663,5184,3622],{"class":1390},[663,5186,2713],{"class":1650},[663,5188,1542],{"class":669},[663,5190,4185],{"class":1390},[663,5192,4193],{"class":669},[663,5194,4185],{"class":1390},[663,5196,5197],{"class":669}," $directory ",[663,5199,1593],{"class":1390},[663,5201,2727],{"class":698},[663,5203,4230],{"class":669},[663,5205,419],{"class":1390},[663,5207,3967],{"class":1390},[663,5209,5211],{"class":665,"line":5210},121,[663,5212,3860],{"class":669},[663,5214,5216,5219,5221,5223,5225,5227,5229,5231,5234,5236,5238,5241],{"class":665,"line":5215},122,[663,5217,5218],{"class":669},"        [$headers, $raw] ",[663,5220,1593],{"class":1390},[663,5222,4013],{"class":683},[663,5224,3995],{"class":1390},[663,5226,2104],{"class":1650},[663,5228,1542],{"class":669},[663,5230,2633],{"class":698},[663,5232,5233],{"class":669},", $path, ",[663,5235,2368],{"class":1365},[663,5237,680],{"class":669},[663,5239,5240],{"class":683},"120",[663,5242,1581],{"class":669},[663,5244,5246,5248,5250,5253,5255,5257,5259,5261,5263,5266,5268,5270,5272,5274,5276],{"class":665,"line":5245},123,[663,5247,3865],{"class":1390},[663,5249,1783],{"class":669},[663,5251,5252],{"class":683},"isset",[663,5254,4782],{"class":669},[663,5256,2811],{"class":698},[663,5258,4787],{"class":669},[663,5260,2853],{"class":1390},[663,5262,4570],{"class":683},[663,5264,5265],{"class":669},"($raw) ",[663,5267,4351],{"class":1390},[663,5269,1783],{"class":669},[663,5271,4219],{"class":1390},[663,5273,4796],{"class":669},[663,5275,2811],{"class":698},[663,5277,5278],{"class":669},"]) {\n",[663,5280,5282,5284,5286,5288,5290,5292],{"class":665,"line":5281},124,[663,5283,3896],{"class":1390},[663,5285,1823],{"class":1390},[663,5287,4622],{"class":683},[663,5289,1542],{"class":669},[663,5291,2884],{"class":698},[663,5293,1581],{"class":669},[663,5295,5297],{"class":665,"line":5296},125,[663,5298,3912],{"class":669},[663,5300,5302,5305,5307,5309,5311,5313,5315,5317],{"class":665,"line":5301},126,[663,5303,5304],{"class":669},"        $disposition ",[663,5306,1593],{"class":1390},[663,5308,4764],{"class":669},[663,5310,2913],{"class":698},[663,5312,2192],{"class":669},[663,5314,1570],{"class":1390},[663,5316,1815],{"class":698},[663,5318,1403],{"class":669},[663,5320,5322,5325,5327,5330,5332,5335,5338,5340,5343,5345,5348,5351,5353,5356,5359,5361],{"class":665,"line":5321},127,[663,5323,5324],{"class":669},"        $name ",[663,5326,1593],{"class":1390},[663,5328,5329],{"class":683}," preg_match",[663,5331,1542],{"class":669},[663,5333,5334],{"class":698},"\"\u002Ffilename",[663,5336,5337],{"class":2995},"\\\\",[663,5339,3070],{"class":1390},[663,5341,5342],{"class":698},"=UTF-8''([^;]",[663,5344,1989],{"class":1390},[663,5346,5347],{"class":698},")\u002Fi\"",[663,5349,5350],{"class":669},", $disposition, $m) ",[663,5352,4280],{"class":1390},[663,5354,5355],{"class":683}," rawurldecode",[663,5357,5358],{"class":669},"($m[",[663,5360,749],{"class":683},[663,5362,5363],{"class":669},"])\n",[663,5365,5367,5370,5372,5374,5376,5379,5381,5384,5386,5388,5391,5393,5395,5397,5399],{"class":665,"line":5366},128,[663,5368,5369],{"class":1390},"            :",[663,5371,1783],{"class":669},[663,5373,3872],{"class":683},[663,5375,1542],{"class":669},[663,5377,5378],{"class":698},"'\u002Ffilename=\"([^\"]",[663,5380,3070],{"class":1390},[663,5382,5383],{"class":698},")\"\u002Fi'",[663,5385,5350],{"class":669},[663,5387,4280],{"class":1390},[663,5389,5390],{"class":669}," $m[",[663,5392,749],{"class":683},[663,5394,2192],{"class":669},[663,5396,419],{"class":1390},[663,5398,2920],{"class":698},[663,5400,1581],{"class":669},[663,5402,5404,5406,5408,5410],{"class":665,"line":5403},129,[663,5405,5324],{"class":669},[663,5407,1593],{"class":1390},[663,5409,2899],{"class":683},[663,5411,5412],{"class":669},"($name);\n",[663,5414,5416,5419,5422,5424,5427,5429],{"class":665,"line":5415},130,[663,5417,5418],{"class":683},"        file_put_contents",[663,5420,5421],{"class":669},"($directory ",[663,5423,497],{"class":1390},[663,5425,5426],{"class":698}," '\u002F'",[663,5428,4269],{"class":1390},[663,5430,5431],{"class":669}," $name, $raw);\n",[663,5433,5435,5437],{"class":665,"line":5434},131,[663,5436,4151],{"class":1390},[663,5438,5439],{"class":669}," $name;\n",[663,5441,5443],{"class":665,"line":5442},132,[663,5444,2071],{"class":669},[663,5446,5448],{"class":665,"line":5447},133,[663,5449,786],{"class":669},[358,5451,3106],{},[654,5453,5455],{"className":3545,"code":5454,"language":3547,"meta":659,"style":659},"\u003C?php\nrequire __DIR__ . '\u002Fvendor\u002Fautoload.php';\nrequire __DIR__ . '\u002FMainTeamClient.php';\n\n$api = new MainTeamClient(getenv('MTO_API_KEY'), getenv('MTO_API_SECRET'));\n\n\u002F\u002F 1. Credentials work? validate-me returns the bare account.\n$me = $api->request('GET', '\u002Fapi-account\u002Fvalidate-me')['data'];\necho \"Signed in as {$me['companyName']}\\n\";\n\n\u002F\u002F 2. Organization ids, looked up once and cached.\n$orgIds = [];\nforeach ($api->paginate('\u002Forganization') as $org) {\n    $orgIds[$org['slug']] = $org['_id'];\n}\n\n\u002F\u002F 3. One student's released reports on coding, downloaded.\n$studentId = '66f2b7c1e4a9d20012ab34cd'; \u002F\u002F a core id, as registration returned it\ntry {\n    foreach ($api->paginate(\"\u002F{$orgIds['coding']}\u002Freport\u002F{$studentId}\") as $report) {\n        $api->download(\"\u002F{$orgIds['coding']}\u002Freport\u002Fdownload\u002F{$report['_id']}\", '\u002Fvar\u002Fexports');\n    }\n} catch (ApiError $e) {\n    error_log(\"{$e->status} {$e->errorCode}: {$e->getMessage()} request_id={$e->requestId}\");\n}\n",[372,5456,5457,5463,5478,5491,5495,5528,5532,5536,5565,5586,5590,5594,5603,5627,5648,5652,5656,5661,5674,5680,5715,5746,5750,5764,5814],{"__ignoreMap":659},[663,5458,5459,5461],{"class":665,"line":666},[663,5460,3554],{"class":1390},[663,5462,3557],{"class":683},[663,5464,5465,5468,5471,5473,5476],{"class":665,"line":673},[663,5466,5467],{"class":1390},"require",[663,5469,5470],{"class":683}," __DIR__",[663,5472,4269],{"class":1390},[663,5474,5475],{"class":698}," '\u002Fvendor\u002Fautoload.php'",[663,5477,1403],{"class":669},[663,5479,5480,5482,5484,5486,5489],{"class":665,"line":690},[663,5481,5467],{"class":1390},[663,5483,5470],{"class":683},[663,5485,4269],{"class":1390},[663,5487,5488],{"class":698}," '\u002FMainTeamClient.php'",[663,5490,1403],{"class":669},[663,5492,5493],{"class":665,"line":704},[663,5494,1451],{"emptyLinePlaceholder":1450},[663,5496,5497,5500,5502,5504,5506,5508,5511,5513,5516,5519,5521,5523,5526],{"class":665,"line":735},[663,5498,5499],{"class":669},"$api ",[663,5501,1593],{"class":1390},[663,5503,1823],{"class":1390},[663,5505,1717],{"class":683},[663,5507,1542],{"class":669},[663,5509,5510],{"class":683},"getenv",[663,5512,1542],{"class":669},[663,5514,5515],{"class":698},"'MTO_API_KEY'",[663,5517,5518],{"class":669},"), ",[663,5520,5510],{"class":683},[663,5522,1542],{"class":669},[663,5524,5525],{"class":698},"'MTO_API_SECRET'",[663,5527,2814],{"class":669},[663,5529,5530],{"class":665,"line":783},[663,5531,1451],{"emptyLinePlaceholder":1450},[663,5533,5534],{"class":665,"line":858},[663,5535,3179],{"class":1384},[663,5537,5538,5541,5543,5546,5548,5550,5552,5554,5556,5558,5560,5562],{"class":665,"line":864},[663,5539,5540],{"class":669},"$me ",[663,5542,1593],{"class":1390},[663,5544,5545],{"class":669}," $api",[663,5547,3995],{"class":1390},[663,5549,2628],{"class":1650},[663,5551,1542],{"class":669},[663,5553,2633],{"class":698},[663,5555,721],{"class":669},[663,5557,3212],{"class":698},[663,5559,4688],{"class":669},[663,5561,4956],{"class":698},[663,5563,5564],{"class":669},"];\n",[663,5566,5567,5570,5573,5576,5579,5582,5584],{"class":665,"line":1491},[663,5568,5569],{"class":683},"echo",[663,5571,5572],{"class":698}," \"Signed in as {",[663,5574,5575],{"class":669},"$me",[663,5577,5578],{"class":698},"['companyName']}",[663,5580,5581],{"class":1390},"\\n",[663,5583,4748],{"class":698},[663,5585,1403],{"class":669},[663,5587,5588],{"class":665,"line":1510},[663,5589,1451],{"emptyLinePlaceholder":1450},[663,5591,5592],{"class":665,"line":1515},[663,5593,3247],{"class":1384},[663,5595,5596,5599,5601],{"class":665,"line":1536},[663,5597,5598],{"class":669},"$orgIds ",[663,5600,1593],{"class":1390},[663,5602,4249],{"class":669},[663,5604,5605,5608,5611,5613,5615,5617,5619,5622,5624],{"class":665,"line":1561},[663,5606,5607],{"class":1390},"foreach",[663,5609,5610],{"class":669}," ($api",[663,5612,3995],{"class":1390},[663,5614,2562],{"class":1650},[663,5616,1542],{"class":669},[663,5618,3285],{"class":698},[663,5620,5621],{"class":669},") ",[663,5623,5097],{"class":1390},[663,5625,5626],{"class":669}," $org) {\n",[663,5628,5629,5632,5635,5638,5640,5643,5646],{"class":665,"line":1584},[663,5630,5631],{"class":669},"    $orgIds[$org[",[663,5633,5634],{"class":698},"'slug'",[663,5636,5637],{"class":669},"]] ",[663,5639,1593],{"class":1390},[663,5641,5642],{"class":669}," $org[",[663,5644,5645],{"class":698},"'_id'",[663,5647,5564],{"class":669},[663,5649,5650],{"class":665,"line":1599},[663,5651,786],{"class":669},[663,5653,5654],{"class":665,"line":1619},[663,5655,1451],{"emptyLinePlaceholder":1450},[663,5657,5658],{"class":665,"line":1632},[663,5659,5660],{"class":1384},"\u002F\u002F 3. One student's released reports on coding, downloaded.\n",[663,5662,5663,5666,5668,5670,5672],{"class":665,"line":1661},[663,5664,5665],{"class":669},"$studentId ",[663,5667,1593],{"class":1390},[663,5669,3355],{"class":698},[663,5671,1468],{"class":669},[663,5673,3360],{"class":1384},[663,5675,5676,5678],{"class":665,"line":1695},[663,5677,3365],{"class":1390},[663,5679,1533],{"class":669},[663,5681,5682,5685,5687,5689,5691,5693,5696,5699,5702,5705,5708,5710,5712],{"class":665,"line":1700},[663,5683,5684],{"class":1390},"    foreach",[663,5686,5610],{"class":669},[663,5688,3995],{"class":1390},[663,5690,2562],{"class":1650},[663,5692,1542],{"class":669},[663,5694,5695],{"class":698},"\"\u002F{",[663,5697,5698],{"class":669},"$orgIds",[663,5700,5701],{"class":698},"['coding']}\u002Freport\u002F{",[663,5703,5704],{"class":669},"$studentId",[663,5706,5707],{"class":698},"}\"",[663,5709,5621],{"class":669},[663,5711,5097],{"class":1390},[663,5713,5714],{"class":669}," $report) {\n",[663,5716,5717,5720,5722,5724,5726,5728,5730,5733,5736,5739,5741,5744],{"class":665,"line":1705},[663,5718,5719],{"class":669},"        $api",[663,5721,3995],{"class":1390},[663,5723,3420],{"class":1650},[663,5725,1542],{"class":669},[663,5727,5695],{"class":698},[663,5729,5698],{"class":669},[663,5731,5732],{"class":698},"['coding']}\u002Freport\u002Fdownload\u002F{",[663,5734,5735],{"class":669},"$report",[663,5737,5738],{"class":698},"['_id']}\"",[663,5740,721],{"class":669},[663,5742,5743],{"class":698},"'\u002Fvar\u002Fexports'",[663,5745,1581],{"class":669},[663,5747,5748],{"class":665,"line":1710},[663,5749,2071],{"class":669},[663,5751,5752,5754,5756,5758,5761],{"class":665,"line":1722},[663,5753,3458],{"class":669},[663,5755,2412],{"class":1390},[663,5757,1783],{"class":669},[663,5759,5760],{"class":683},"ApiError",[663,5762,5763],{"class":669}," $e) {\n",[663,5765,5766,5769,5771,5774,5777,5779,5781,5784,5786,5788,5791,5794,5796,5798,5801,5804,5806,5808,5810,5812],{"class":665,"line":1755},[663,5767,5768],{"class":683},"    error_log",[663,5770,1542],{"class":669},[663,5772,5773],{"class":698},"\"{",[663,5775,5776],{"class":669},"$e",[663,5778,3995],{"class":1390},[663,5780,1545],{"class":669},[663,5782,5783],{"class":698},"} {",[663,5785,5776],{"class":669},[663,5787,3995],{"class":1390},[663,5789,5790],{"class":669},"errorCode",[663,5792,5793],{"class":698},"}: {",[663,5795,5776],{"class":669},[663,5797,3995],{"class":1390},[663,5799,5800],{"class":1650},"getMessage",[663,5802,5803],{"class":698},"()} request_id={",[663,5805,5776],{"class":669},[663,5807,3995],{"class":1390},[663,5809,3507],{"class":669},[663,5811,5707],{"class":698},[663,5813,1581],{"class":669},[663,5815,5816],{"class":665,"line":1760},[663,5817,786],{"class":669},[358,5819,5820,5821,5824],{},"This client buffers a download in memory before writing it. For very large files, point\n",[372,5822,5823],{},"CURLOPT_FILE"," at a temporary file instead, check the status, then rename the file into place.",[404,5826,5828],{"id":5827},"what-the-minimal-clients-leave-out","What the minimal clients leave out",[358,5830,5831],{},"Add these before you run either client in production:",[934,5833,5834,5853,5865,5879,5895],{},[365,5835,5836,5839,5840,5842,5843,5845,5846,5849,5850,5852],{},[528,5837,5838],{},"Retries."," Wrap ",[372,5841,2628],{}," with backoff for network errors and ",[372,5844,1105],{},", and a wait of\n",[372,5847,5848],{},"retryAfter"," seconds (or 60 when it's missing) for ",[372,5851,1323],{},". Follow the retry table in rule 9\nabove.",[365,5854,5855,5858,5859,5861,5862,5864],{},[528,5856,5857],{},"A 401 retry."," On ",[372,5860,1189],{},", drop the cached token, sign a new one, and retry once. A second ",[372,5863,1189],{},"\nis a configuration problem.",[365,5866,5867,5870,5871,5878],{},[528,5868,5869],{},"Logging."," Log method, path, status, duration and request id. ",[528,5872,5873,5874,5877],{},"Redact the ",[372,5875,5876],{},"Authorization","\nheader",", and never log a sign-in link URL.",[365,5880,5881,5884,5885,1003,5888,5891,5892,497],{},[528,5882,5883],{},"Deprecation headers."," Log any ",[372,5886,5887],{},"Deprecation",[372,5889,5890],{},"Sunset"," response header so a route that is\ngoing away doesn't surprise you. Deprecations are announced at least 6 months ahead in the\n",[395,5893,5894],{"href":349},"changelog",[365,5896,5897,872,5900,5905,5906,5909,5910,497],{},[528,5898,5899],{},"A health check.",[395,5901,5902],{"href":132},[372,5903,5904],{},"GET \u002Fv1\u002Fhealth"," needs no token and doesn't count\nagainst your rate limit. It answers ",[372,5907,5908],{},"{ \"success\": true, \"message\": \"Request completed successfully.\", \"data\": { \"status\": \"ok\" } }","\nwhile the API is up. To check your own credentials as well, call ",[372,5911,633],{},[404,5913,5915],{"id":5914},"conformance-checklist","Conformance checklist",[358,5917,5918],{},"Run through this list before you rely on your client. Each item is a rule the API enforces or a\nbehavior it has.",[409,5920,19],{"id":5921},"authentication",[934,5923,5926,5939,5957,5973,5983,5993,6003],{"className":5924},[5925],"contains-task-list",[365,5927,5930,5935,5936,5938],{"className":5928},[5929],"task-list-item",[5931,5932],"input",{"disabled":1450,"type":5933,"ariaLabel":5934},"checkbox","The apiSecret is read from an environment variable or secret store, on the server only, and never written to a log, an error report or a response."," The ",[372,5937,418],{}," is read from an environment variable or secret store, on the server only,\nand never written to a log, an error report or a response.",[365,5940,5942,5945,5946,5948,5949,5951,5952,5948,5954,5956],{"className":5941},[5929],[5931,5943],{"disabled":1450,"type":5933,"ariaLabel":5944},"Tokens are HS256, with kid = apiKey in the header and sub = apiKey in the payload."," Tokens are HS256, with ",[372,5947,482],{}," = ",[372,5950,488],{}," in the header and ",[372,5953,507],{},[372,5955,488],{}," in the payload.",[365,5958,5960,5963,5964,375,5966,5968,5969,5972],{"className":5959},[5929],[5931,5961],{"disabled":1450,"type":5933,"ariaLabel":5962},"Every token has numeric iat and exp in whole seconds, and exp − iat is at most 3600."," Every token has numeric ",[372,5965,523],{},[372,5967,539],{}," in whole seconds, and ",[372,5970,5971],{},"exp − iat"," is at most 3600.",[365,5974,5976,5979,5980,5982],{"className":5975},[5929],[5931,5977],{"disabled":1450,"type":5933,"ariaLabel":5978},"A token is reused until about 60 seconds before exp, then replaced."," A token is reused until about 60 seconds before ",[372,5981,539],{},", then replaced.",[365,5984,5986,5989,5990,5992],{"className":5985},[5929],[5931,5987],{"disabled":1450,"type":5933,"ariaLabel":5988},"The server clock is synchronized. iat is never more than 30 seconds ahead of real time."," The server clock is synchronized. ",[372,5991,523],{}," is never more than 30 seconds ahead of real time.",[365,5994,5996,5999,6000,6002],{"className":5995},[5929],[5931,5997],{"disabled":1450,"type":5933,"ariaLabel":5998},"A 401 triggers at most one re-sign and retry, never a loop."," A ",[372,6001,1189],{}," triggers at most one re-sign and retry, never a loop.",[365,6004,6006,6009,6010,6012],{"className":6005},[5929],[5931,6007],{"disabled":1450,"type":5933,"ariaLabel":6008},"Revoking a token (POST \u002Fv1\u002Fapi-account\u002Frevoke-token) is wired up for shutdown or a suspected leak, if you keep long-lived tokens."," Revoking a token (",[372,6011,625],{},") is wired up for shutdown or a\nsuspected leak, if you keep long-lived tokens.",[409,6014,6016],{"id":6015},"requests","Requests",[934,6018,6020,6033,6046,6056,6066,6079],{"className":6019},[5925],[365,6021,6023,6026,6027,6029,6030,6032],{"className":6022},[5929],[5931,6024],{"disabled":1450,"type":5933,"ariaLabel":6025},"The base URL is https:\u002F\u002Fapi.main-team.org\u002Fv1 (or https:\u002F\u002Fapisnd.main-team.org\u002Fv1 for the sandbox), fixed in configuration you control."," The base URL is ",[372,6028,941],{}," (or ",[372,6031,945],{}," for the\nsandbox), fixed in configuration you control.",[365,6034,6036,6039,6040,6042,6043,6045],{"className":6035},[5929],[5931,6037],{"disabled":1450,"type":5933,"ariaLabel":6038},"Organization paths use the _id from GET \u002Fv1\u002Forganization, never a slug."," Organization paths use the ",[372,6041,957],{}," from ",[372,6044,963],{},", never a slug.",[365,6047,6049,6052,6053,6055],{"className":6048},[5929],[5931,6050],{"disabled":1450,"type":5933,"ariaLabel":6051},"Bodies are UTF-8 JSON with Content-Type: application\u002Fjson, under 100 kB."," Bodies are UTF-8 JSON with ",[372,6054,983],{},", under 100 kB.",[365,6057,6059,6062,6063,6065],{"className":6058},[5929],[5931,6060],{"disabled":1450,"type":5933,"ariaLabel":6061},"Bodies carry only documented fields. A 400 that names a property is treated as a bug in the caller."," Bodies carry only documented fields. A ",[372,6064,1197],{}," that names a property is treated as a bug in the\ncaller.",[365,6067,6069,6072,6073,6075,6076,6078],{"className":6068},[5929],[5931,6070],{"disabled":1450,"type":5933,"ariaLabel":6071},"Every request sends an X-Request-Id (1 to 256 characters of A–Z a–z 0–9 . _ : ; = + \u002F @ -), or the response's id is logged."," Every request sends an ",[372,6074,916],{}," (1 to 256 characters of ",[372,6077,927],{},"),\nor the response's id is logged.",[365,6080,6082,6085],{"className":6081},[5929],[5931,6083],{"disabled":1450,"type":5933,"ariaLabel":6084},"Connect and read timeouts are set, with a longer read timeout for downloads."," Connect and read timeouts are set, with a longer read timeout for downloads.",[409,6087,6089],{"id":6088},"responses","Responses",[934,6091,6093,6103,6112,6122,6141,6154,6161],{"className":6092},[5925],[365,6094,6096,6099,6100,497],{"className":6095},[5929],[5931,6097],{"disabled":1450,"type":5933,"ariaLabel":6098},"Success bodies are unwrapped from { success, message, data, pagination? }."," Success bodies are unwrapped from ",[372,6101,6102],{},"{ success, message, data, pagination? }",[365,6104,6106,872,6109,6111],{"className":6105},[5929],[5931,6107],{"disabled":1450,"type":5933,"ariaLabel":6108},"validate-me is handled as a bare object.",[372,6110,633],{}," is handled as a bare object.",[365,6113,6115,5999,6118,6121],{"className":6114},[5929],[5931,6116],{"disabled":1450,"type":5933,"ariaLabel":6117},"A 404 on a single read is treated as not found, whether the record is missing or not yours.",[372,6119,6120],{},"404"," on a single read is treated as not found, whether the record is missing or not yours.",[365,6123,6125,872,6128,375,6131,6133,6134,6137,6138,6140],{"className":6124},[5929],[5931,6126],{"disabled":1450,"type":5933,"ariaLabel":6127},"201 and 200 on POST \u002Fapplication are both treated as success. 200 means the application already existed.",[372,6129,6130],{},"201",[372,6132,637],{}," on ",[372,6135,6136],{},"POST \u002Fapplication"," are both treated as success. ",[372,6139,637],{}," means the\napplication already existed.",[365,6142,6144,6147,6148,6150,6151,497],{"className":6143},[5929],[5931,6145],{"disabled":1450,"type":5933,"ariaLabel":6146},"Error handling branches on status and error.code, never on error.message."," Error handling branches on status and ",[372,6149,884],{},", never on ",[372,6152,6153],{},"error.message",[365,6155,6157,6160],{"className":6156},[5929],[5931,6158],{"disabled":1450,"type":5933,"ariaLabel":6159},"Unknown response fields and unknown enum values are ignored, not rejected."," Unknown response fields and unknown enum values are ignored, not rejected.",[365,6162,6164,6167,6168,6171,6172,497],{"className":6163},[5929],[5931,6165],{"disabled":1450,"type":5933,"ariaLabel":6166},"Students are matched on mainId, not on an organization's _id."," Students are matched on ",[372,6169,6170],{},"mainId",", not on an organization's ",[372,6173,957],{},[409,6175,39],{"id":791},[934,6177,6179,6191,6201],{"className":6178},[5925],[365,6180,6182,872,6185,6187,6188,6190],{"className":6181},[5929],[5931,6183],{"disabled":1450,"type":5933,"ariaLabel":6184},"limit is at most 100, and page starts at 1.",[372,6186,1025],{}," is at most 100, and ",[372,6189,1021],{}," starts at 1.",[365,6192,6194,6197,6198,6200],{"className":6193},[5929],[5931,6195],{"disabled":1450,"type":5933,"ariaLabel":6196},"Walking stops at pagination.totalPages, including when it is 0."," Walking stops at ",[372,6199,1032],{},", including when it is 0.",[365,6202,6204,6207,6208,497],{"className":6203},[5929],[5931,6205],{"disabled":1450,"type":5933,"ariaLabel":6206},"Long walks de-duplicate by _id."," Long walks de-duplicate by ",[372,6209,957],{},[409,6211,6213],{"id":6212},"downloads","Downloads",[934,6215,6217,6224,6237,6244],{"className":6216},[5925],[365,6218,6220,6223],{"className":6219},[5929],[5931,6221],{"disabled":1450,"type":5933,"ariaLabel":6222},"The status is checked before any bytes are written. An error body is JSON."," The status is checked before any bytes are written. An error body is JSON.",[365,6225,6227,6230,6231,6233,6234,6236],{"className":6226},[5929],[5931,6228],{"disabled":1450,"type":5933,"ariaLabel":6229},"The file name comes from filename* first, then filename, with any path stripped."," The file name comes from ",[372,6232,1078],{}," first, then ",[372,6235,1082],{},", with any path stripped.",[365,6238,6240,6243],{"className":6239},[5929],[5931,6241],{"disabled":1450,"type":5933,"ariaLabel":6242},"A short or interrupted body is treated as a failed download and the partial file discarded."," A short or interrupted body is treated as a failed download and the partial file discarded.",[365,6245,6247,5999,6250,6252],{"className":6246},[5929],[5931,6248],{"disabled":1450,"type":5933,"ariaLabel":6249},"A 404 is treated as \"not available\": missing, not released, not yours and no file all give the same answer.",[372,6251,6120],{}," is treated as \"not available\": missing, not released, not yours and no file all give\nthe same answer.",[409,6254,6256],{"id":6255},"rate-limits-and-retries","Rate limits and retries",[934,6258,6260,6272,6281,6291,6304],{"className":6259},[5925],[365,6261,6263,5999,6266,6268,6269,6271],{"className":6262},[5929],[5931,6264],{"disabled":1450,"type":5933,"ariaLabel":6265},"A 429 waits Retry-After seconds, or 60 when the header is missing, before that operation is called again.",[372,6267,1323],{}," waits ",[372,6270,1185],{}," seconds, or 60 when the header is missing, before that operation\nis called again.",[365,6273,6275,872,6278,6280],{"className":6274},[5929],[5931,6276],{"disabled":1450,"type":5933,"ariaLabel":6277},"X-RateLimit-Remaining is used to pace bulk jobs, per operation.",[372,6279,1162],{}," is used to pace bulk jobs, per operation.",[365,6282,6284,6287,6288,6290],{"className":6283},[5929],[5931,6285],{"disabled":1450,"type":5933,"ariaLabel":6286},"Network errors and 5xx are retried with exponential backoff and jitter, on safe requests only."," Network errors and ",[372,6289,1105],{}," are retried with exponential backoff and jitter, on safe requests\nonly.",[365,6292,6294,872,6297,6299,6300,6303],{"className":6293},[5929],[5931,6295],{"disabled":1450,"type":5933,"ariaLabel":6296},"POST \u002Fv1\u002Fstudent is never retried blindly. A 409 is resolved by fetching the named student.",[372,6298,1287],{}," is never retried blindly. A ",[372,6301,6302],{},"409"," is resolved by fetching the named\nstudent.",[365,6305,6307,6310,6311,6313],{"className":6306},[5929],[5931,6308],{"disabled":1450,"type":5933,"ariaLabel":6309},"Other 4xx answers are not retried."," Other ",[372,6312,1102],{}," answers are not retried.",[409,6315,6317],{"id":6316},"sign-in-links-and-security","Sign-in links and security",[934,6319,6321,6328,6335,6344],{"className":6320},[5925],[365,6322,6324,6327],{"className":6323},[5929],[5931,6325],{"disabled":1450,"type":5933,"ariaLabel":6326},"A sign-in link is minted when the student asks for it, and the browser is redirected at once."," A sign-in link is minted when the student asks for it, and the browser is redirected at once.",[365,6329,6331,6334],{"className":6330},[5929],[5931,6332],{"disabled":1450,"type":5933,"ariaLabel":6333},"Link URLs are never stored, logged, emailed, retried or prefetched."," Link URLs are never stored, logged, emailed, retried or prefetched.",[365,6336,6338,5935,6341,6343],{"className":6337},[5929],[5931,6339],{"disabled":1450,"type":5933,"ariaLabel":6340},"The Authorization header is redacted everywhere it could be logged.",[372,6342,5876],{}," header is redacted everywhere it could be logged.",[365,6345,6347,6350,6351,6353],{"className":6346},[5929],[5931,6348],{"disabled":1450,"type":5933,"ariaLabel":6349},"Nothing that holds the apiSecret runs in a browser or a distributed app."," Nothing that holds the ",[372,6352,418],{}," runs in a browser or a distributed app.",[404,6355,6357],{"id":6356},"testing-your-client","Testing your client",[358,6359,6360,6361,6363],{},"Test against the ",[395,6362,950],{"href":949}," first, with your sandbox credentials. In\nproduction, start with calls that change nothing:",[362,6365,6366,6371,6376,6381,6393,6404],{},[365,6367,6368,6370],{},[372,6369,5904],{},": reachability, no token needed.",[365,6372,6373,6375],{},[372,6374,877],{},": token signing, and the bare-object exception.",[365,6377,6378,6380],{},[372,6379,963],{},": the envelope and pagination.",[365,6382,6383,680,6386,6388,6389,6392],{},[372,6384,6385],{},"GET \u002Fv1\u002Fcountry?limit=500",[372,6387,1025],{}," clamping. ",[372,6390,6391],{},"pagination.limit"," comes back as 100.",[365,6394,6395,6396,6398,6399,6401,6402,497],{},"A token with ",[372,6397,5971],{}," of 3601 seconds: expect ",[372,6400,602],{}," with ",[372,6403,614],{},[365,6405,6406,6409,6410,721,6412,497],{},[372,6407,6408],{},"GET \u002Fv1\u002F000000000000000000000000\u002Fexam",": expect ",[372,6411,901],{},[372,6413,970],{},[358,6415,6416,6417,497],{},"Every call you make against production counts against your rate limit and, for anything that\nwrites, changes real data. The sandbox has its own accounts, seeded reference data, no emails and\nno real payments, so run the writes there. See ",[395,6418,16],{"href":949},[358,6420,6421,6422,6424,6425,6427],{},"Stuck? The ",[395,6423,342],{"href":343}," page is organized by symptom, and\n",[395,6426,345],{"href":346}," says what to include when you write to us.",[6429,6430,6431],"style",{},"html pre.shiki code .suds8, html code.shiki .suds8{--shiki-default:#0E1116;--shiki-dark:#F0F3F6}html pre.shiki code .sne4z, html code.shiki .sne4z{--shiki-default:#024C1A;--shiki-dark:#72F088}html pre.shiki code .s-5SL, html code.shiki .s-5SL{--shiki-default:#023B95;--shiki-dark:#91CBFF}html pre.shiki code .sT6z2, html code.shiki .sT6z2{--shiki-default:#032563;--shiki-dark:#ADDCFF}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 .soyes, html code.shiki .soyes{--shiki-default:#702C00;--shiki-dark:#FFB757}html pre.shiki code .sLBg1, html code.shiki .sLBg1{--shiki-default:#66707B;--shiki-dark:#BDC4CC}html pre.shiki code .sHUrx, html code.shiki .sHUrx{--shiki-default:#A0111F;--shiki-dark:#FF9492}html pre.shiki code .sKwhi, html code.shiki .sKwhi{--shiki-default:#622CBC;--shiki-dark:#DBB7FF}html pre.shiki code .sJaa3, html code.shiki .sJaa3{--shiki-default:#024C1A;--shiki-default-font-weight:bold;--shiki-dark:#72F088;--shiki-dark-font-weight:bold}",{"title":659,"searchDepth":673,"depth":690,"links":6433},[6434,6446,6447,6448,6449,6458],{"id":406,"depth":673,"text":407,"children":6435},[6436,6437,6438,6439,6440,6441,6442,6443,6444,6445],{"id":411,"depth":690,"text":412},{"id":648,"depth":690,"text":649},{"id":909,"depth":690,"text":910},{"id":931,"depth":690,"text":932},{"id":1015,"depth":690,"text":1016},{"id":1044,"depth":690,"text":1045},{"id":1112,"depth":690,"text":1113},{"id":1124,"depth":690,"text":1125},{"id":1203,"depth":690,"text":1204},{"id":1336,"depth":690,"text":1337},{"id":1346,"depth":673,"text":1347},{"id":3518,"depth":673,"text":3519},{"id":5827,"depth":673,"text":5828},{"id":5914,"depth":673,"text":5915,"children":6450},[6451,6452,6453,6454,6455,6456,6457],{"id":5921,"depth":690,"text":19},{"id":6015,"depth":690,"text":6016},{"id":6088,"depth":690,"text":6089},{"id":791,"depth":690,"text":39},{"id":6212,"depth":690,"text":6213},{"id":6255,"depth":690,"text":6256},{"id":6316,"depth":690,"text":6317},{"id":6356,"depth":673,"text":6357},"Everything a Main Team API client must do, a minimal client in Node.js and in PHP, and a checklist to test your own against.","md",{},[6463,6464,6465,6466,6467,6468,6469,6470],"getHealth","getCurrentApiAccount","revokeToken","listOrganizations","listStudents","getStudent","downloadCertificate","downloadReport","40",{"title":353,"description":6459},"api\u002Fclients\u002Fbuild-your-own","x_JXtdGjLW4LtUEcOtwt9YgouJMbGdGMf44Ia3rs60E",[6476,6481,6486,6489,6493,6497,6500,6505],{"operationId":6463,"slug":6477,"method":133,"path":6478,"tag":127,"summary":131,"deprecated":134,"public":1450,"permission":6479,"scope":6480,"order":666},"get-health","\u002Fv1\u002Fhealth",null,"public",{"operationId":6464,"slug":6482,"method":133,"path":6483,"tag":77,"summary":143,"deprecated":134,"public":134,"permission":6484,"scope":6485,"order":690},"get-current-api-account","\u002Fv1\u002Fapi-account\u002Fvalidate-me","api\u002F*:$org:$ID","flat",{"operationId":6465,"slug":6487,"method":141,"path":6488,"tag":77,"summary":139,"deprecated":134,"public":134,"permission":6484,"scope":6485,"order":673},"revoke-token","\u002Fv1\u002Fapi-account\u002Frevoke-token",{"operationId":6466,"slug":6490,"method":133,"path":6491,"tag":74,"summary":161,"deprecated":134,"public":134,"permission":6492,"scope":6485,"order":864},"list-organizations","\u002Fv1\u002Forganization","organization\u002Fread:$org:$ID",{"operationId":6467,"slug":6494,"method":133,"path":6495,"tag":62,"summary":170,"deprecated":134,"public":134,"permission":6496,"scope":6485,"order":1510},"list-students","\u002Fv1\u002Fstudent","student\u002Fread:$org:$ID",{"operationId":6468,"slug":6498,"method":133,"path":6499,"tag":62,"summary":185,"deprecated":134,"public":134,"permission":6496,"scope":6485,"order":1599},"get-student","\u002Fv1\u002Fstudent\u002F{studentId}",{"operationId":6469,"slug":6501,"method":133,"path":6502,"tag":256,"summary":260,"deprecated":134,"public":134,"permission":6503,"scope":6504,"order":1977},"download-certificate","\u002Fv1\u002F{organizationId}\u002Fcertificate\u002Fdownload\u002F{certificateId}","certificate\u002Fread:$org:$ID","organization",{"operationId":6470,"slug":6506,"method":133,"path":6507,"tag":256,"summary":266,"deprecated":134,"public":134,"permission":6508,"scope":6504,"order":2014},"download-report","\u002Fv1\u002F{organizationId}\u002Freport\u002Fdownload\u002F{reportId}","report\u002Fread:$org:$ID",1791554615393]