[{"data":1,"prerenderedAt":3138},["ShallowReactive",2],{"api-nav":3,"api-guide:\u002Fapi\u002Fclients\u002Fphp":351,"api-spec:guide:\u002Fapi\u002Fclients\u002Fphp":3093},[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":3075,"extension":3076,"meta":3077,"navTitle":311,"navigation":544,"operations":3078,"order":3089,"path":312,"section":302,"seo":3090,"status":14,"stem":3091,"__hash__":3092},"apiGuides\u002Fapi\u002Fclients\u002Fphp.md","PHP client",{"type":355,"value":356,"toc":3055},"minimark",[357,369,372,377,418,422,435,460,464,467,522,626,629,641,663,667,679,730,754,910,928,932,938,1008,1011,1082,1088,1109,1113,1119,1124,1376,1386,1402,1413,1555,1560,1564,1567,1661,1664,1759,1778,1790,1794,1811,1840,1845,1849,1885,1944,1950,1954,1968,2107,2126,2145,2153,2260,2263,2269,2372,2387,2462,2467,2471,2480,2575,2587,2591,2619,2625,2853,2871,2875,2878,2928,2939,2942,3010,3014,3028,3032,3051],[358,359,360,364,365,368],"p",{},[361,362,363],"code",{},"main-team\u002Fapi-client"," is the official PHP client for the Main Team API. This page takes you from\n",[361,366,367],{},"composer require"," to a complete integration: you register a student, send them into the panel,\nenter them for an exam, and download their certificates. Each step explains which API rule it\nrelies on, so you know what to expect when a call fails.",[358,370,371],{},"The package README is the full method reference. This page covers what you need for a working\nintegration and the API behavior behind each part.",[373,374,376],"h2",{"id":375},"requirements","Requirements",[378,379,380,396,402],"ul",{},[381,382,383,387,388,391,392,395],"li",{},[384,385,386],"strong",{},"PHP 8.1 or newer",", with ",[361,389,390],{},"ext-curl"," and ",[361,393,394],{},"ext-json",". Both are standard.",[381,397,398,401],{},[384,399,400],{},"No other dependencies",", so the client doesn't pull an HTTP stack into your project.",[381,403,404,407,408,411,412,417],{},[384,405,406],{},"A server."," The client holds your ",[361,409,410],{},"apiSecret",", so it runs on your backend only. See\n",[413,414,416],"a",{"href":415},"\u002Fapi\u002Fclients#server-side-only","Server-side only",".",[373,419,421],{"id":420},"install","Install",[358,423,424,425,391,428,430,431,417],{},"The package is private. Access comes with your API credentials: the access instructions you\nreceive with your ",[361,426,427],{},"apiKey",[361,429,410],{}," tell you how to configure Composer for it. If you have\ncredentials but no package access, write to ",[413,432,434],{"href":433},"mailto:info@main-team.org","info@main-team.org",[436,437,442],"pre",{"className":438,"code":439,"language":440,"meta":441,"style":441},"language-bash shiki shiki-themes github-light-high-contrast github-dark-high-contrast","composer require main-team\u002Fapi-client\n","bash","",[361,443,444],{"__ignoreMap":441},[445,446,449,453,457],"span",{"class":447,"line":448},"line",1,[445,450,452],{"class":451},"soyes","composer",[445,454,456],{"class":455},"sT6z2"," require",[445,458,459],{"class":455}," main-team\u002Fapi-client\n",[373,461,463],{"id":462},"set-up-the-client","Set up the client",[358,465,466],{},"You need two values, both from your API credentials:",[468,469,470,486],"table",{},[471,472,473],"thead",{},[474,475,476,480,483],"tr",{},[477,478,479],"th",{},"Value",[477,481,482],{},"What it is",[477,484,485],{},"Where it goes",[487,488,489,506],"tbody",{},[474,490,491,496,503],{},[492,493,494],"td",{},[361,495,427],{},[492,497,498,499,502],{},"Your public account identifier, ",[361,500,501],{},"key_"," followed by 24 characters.",[492,504,505],{},"Anywhere. It's not a secret.",[474,507,508,512,519],{},[492,509,510],{},[361,511,410],{},[492,513,514,515,518],{},"Your signing key, ",[361,516,517],{},"secret_"," followed by random characters. Shown to you once, and it can't be recovered.",[492,520,521],{},"An environment variable or a secret store, on the server only.",[436,523,527],{"className":524,"code":525,"language":526,"meta":441,"style":441},"language-php shiki shiki-themes github-light-high-contrast github-dark-high-contrast","\u003C?php\n\nuse MainTeam\\ApiClient\\MtoClient;\n\n$client = new MtoClient(\n    apiKey: getenv('MTO_API_KEY'),\n    apiSecret: getenv('MTO_API_SECRET'),\n);\n","php",[361,528,529,539,546,559,564,582,603,620],{"__ignoreMap":441},[445,530,531,535],{"class":447,"line":448},[445,532,534],{"class":533},"sHUrx","\u003C?",[445,536,538],{"class":537},"s-5SL","php\n",[445,540,542],{"class":447,"line":541},2,[445,543,545],{"emptyLinePlaceholder":544},true,"\n",[445,547,549,552,555],{"class":447,"line":548},3,[445,550,551],{"class":533},"use",[445,553,554],{"class":537}," MainTeam\\ApiClient\\MtoClient",[445,556,558],{"class":557},"suds8",";\n",[445,560,562],{"class":447,"line":561},4,[445,563,545],{"emptyLinePlaceholder":544},[445,565,567,570,573,576,579],{"class":447,"line":566},5,[445,568,569],{"class":557},"$client ",[445,571,572],{"class":533},"=",[445,574,575],{"class":533}," new",[445,577,578],{"class":537}," MtoClient",[445,580,581],{"class":557},"(\n",[445,583,585,588,591,594,597,600],{"class":447,"line":584},6,[445,586,587],{"class":451},"    apiKey",[445,589,590],{"class":557},": ",[445,592,593],{"class":537},"getenv",[445,595,596],{"class":557},"(",[445,598,599],{"class":455},"'MTO_API_KEY'",[445,601,602],{"class":557},"),\n",[445,604,606,609,611,613,615,618],{"class":447,"line":605},7,[445,607,608],{"class":451},"    apiSecret",[445,610,590],{"class":557},[445,612,593],{"class":537},[445,614,596],{"class":557},[445,616,617],{"class":455},"'MTO_API_SECRET'",[445,619,602],{"class":557},[445,621,623],{"class":447,"line":622},8,[445,624,625],{"class":557},");\n",[358,627,628],{},"Build the client once per request cycle, or once per worker in a long-running process, and reuse\nit. It signs short-lived tokens with your secret for you, so there's no token endpoint to call\nand no token for you to manage.",[630,631,633],"callout",{"type":632},"security",[358,634,635,638,639,417],{},[384,636,637],{},"The secret never leaves your process."," Only the signed token travels, and a token expires\nwithin an hour at most. Keep the secret out of source control, out of anything served to a browser,\nand out of logs, including exception traces that dump constructor arguments. If it leaks, ask your\noperator to deactivate the account. There is no secret rotation; see ",[413,640,51],{"href":52},[358,642,643,646,647,650,651,655,656,659,660,417],{},[384,644,645],{},"There is no base URL to set."," The client talks to ",[361,648,649],{},"https:\u002F\u002Fapi.main-team.org\u002Fv1"," only, because\nany other host would receive a bearer token good for your whole account. See\n",[413,652,654],{"href":653},"\u002Fapi\u002Fclients#why-the-base-url-is-locked","Why the base URL is locked",". That includes the sandbox:\nto call ",[361,657,658],{},"https:\u002F\u002Fapisnd.main-team.org\u002Fv1",", send plain HTTPS requests, or use the client from\n",[413,661,662],{"href":315},"Build your own client",[373,664,666],{"id":665},"check-that-your-credentials-work","Check that your credentials work",[358,668,669,672,673,678],{},[361,670,671],{},"whoami()"," calls ",[413,674,675],{"href":144},[361,676,677],{},"GET \u002Fv1\u002Fapi-account\u002Fvalidate-me"," and\nreturns the account your token belongs to:",[436,680,682],{"className":524,"code":681,"language":526,"meta":441,"style":441},"$account = $client->account()->whoami();\necho $account->companyName, PHP_EOL;\n",[361,683,684,712],{"__ignoreMap":441},[445,685,686,689,691,694,697,701,704,706,709],{"class":447,"line":448},[445,687,688],{"class":557},"$account ",[445,690,572],{"class":533},[445,692,693],{"class":557}," $client",[445,695,696],{"class":533},"->",[445,698,700],{"class":699},"sKwhi","account",[445,702,703],{"class":557},"()",[445,705,696],{"class":533},[445,707,708],{"class":699},"whoami",[445,710,711],{"class":557},"();\n",[445,713,714,717,720,722,725,728],{"class":447,"line":541},[445,715,716],{"class":537},"echo",[445,718,719],{"class":557}," $account",[445,721,696],{"class":533},[445,723,724],{"class":557},"companyName, ",[445,726,727],{"class":537},"PHP_EOL",[445,729,558],{"class":557},[358,731,732,733,736,737,736,739,736,742,736,745,391,748,751,752,417],{},"On the wire, this is the one JSON route without the response envelope. It returns the account\nobject itself: ",[361,734,735],{},"_id",", ",[361,738,427],{},[361,740,741],{},"companyName",[361,743,744],{},"scopes",[361,746,747],{},"roles",[361,749,750],{},"isActive",", and never\n",[361,753,410],{},[436,755,759],{"className":756,"code":757,"language":758,"meta":441,"style":441},"language-json shiki shiki-themes github-light-high-contrast github-dark-high-contrast","{\n  \"_id\": \"66f1a2b3c4d5e6f708192a3b\",\n  \"apiKey\": \"key_Q2hvb3NlQW5vdGhlcktleUhl\",\n  \"companyName\": \"Example Learning Ltd\",\n  \"scopes\": [],\n  \"roles\": [\n    { \"effect\": \"allow\", \"action\": \"*\u002Fread\", \"target\": \"*\" },\n    { \"effect\": \"allow\", \"action\": \"api\u002F*\", \"target\": \"mto\" }\n  ],\n  \"isActive\": true\n}\n","json",[361,760,761,766,780,792,804,812,820,856,887,893,904],{"__ignoreMap":441},[445,762,763],{"class":447,"line":448},[445,764,765],{"class":557},"{\n",[445,767,768,772,774,777],{"class":447,"line":541},[445,769,771],{"class":770},"sne4z","  \"_id\"",[445,773,590],{"class":557},[445,775,776],{"class":455},"\"66f1a2b3c4d5e6f708192a3b\"",[445,778,779],{"class":557},",\n",[445,781,782,785,787,790],{"class":447,"line":548},[445,783,784],{"class":770},"  \"apiKey\"",[445,786,590],{"class":557},[445,788,789],{"class":455},"\"key_Q2hvb3NlQW5vdGhlcktleUhl\"",[445,791,779],{"class":557},[445,793,794,797,799,802],{"class":447,"line":561},[445,795,796],{"class":770},"  \"companyName\"",[445,798,590],{"class":557},[445,800,801],{"class":455},"\"Example Learning Ltd\"",[445,803,779],{"class":557},[445,805,806,809],{"class":447,"line":566},[445,807,808],{"class":770},"  \"scopes\"",[445,810,811],{"class":557},": [],\n",[445,813,814,817],{"class":447,"line":584},[445,815,816],{"class":770},"  \"roles\"",[445,818,819],{"class":557},": [\n",[445,821,822,825,828,830,833,835,838,840,843,845,848,850,853],{"class":447,"line":605},[445,823,824],{"class":557},"    { ",[445,826,827],{"class":770},"\"effect\"",[445,829,590],{"class":557},[445,831,832],{"class":455},"\"allow\"",[445,834,736],{"class":557},[445,836,837],{"class":770},"\"action\"",[445,839,590],{"class":557},[445,841,842],{"class":455},"\"*\u002Fread\"",[445,844,736],{"class":557},[445,846,847],{"class":770},"\"target\"",[445,849,590],{"class":557},[445,851,852],{"class":455},"\"*\"",[445,854,855],{"class":557}," },\n",[445,857,858,860,862,864,866,868,870,872,875,877,879,881,884],{"class":447,"line":622},[445,859,824],{"class":557},[445,861,827],{"class":770},[445,863,590],{"class":557},[445,865,832],{"class":455},[445,867,736],{"class":557},[445,869,837],{"class":770},[445,871,590],{"class":557},[445,873,874],{"class":455},"\"api\u002F*\"",[445,876,736],{"class":557},[445,878,847],{"class":770},[445,880,590],{"class":557},[445,882,883],{"class":455},"\"mto\"",[445,885,886],{"class":557}," }\n",[445,888,890],{"class":447,"line":889},9,[445,891,892],{"class":557},"  ],\n",[445,894,896,899,901],{"class":447,"line":895},10,[445,897,898],{"class":770},"  \"isActive\"",[445,900,590],{"class":557},[445,902,903],{"class":537},"true\n",[445,905,907],{"class":447,"line":906},11,[445,908,909],{"class":557},"}\n",[358,911,912,913,916,917,919,920,923,924,927],{},"A ",[361,914,915],{},"401 unauthorized"," here means the token was refused. Every token failure gives that same answer,\nso check the key, the secret and your server clock against ",[413,918,19],{"href":20},".\nA ",[361,921,922],{},"403 forbidden"," means your roles lack ",[361,925,926],{},"api\u002F*"," on mto, which this route needs. Other routes may\nstill work.",[373,929,931],{"id":930},"choosing-an-organization","Choosing an organization",[358,933,934,935,937],{},"Most operations belong to one organization: mto (the core record), stem, hilingua, neo, gmath or\ncoding. The API's paths take the organization's ",[361,936,735],{},", never its slug. The client resolves slugs\nfor you:",[436,939,941],{"className":524,"code":940,"language":526,"meta":441,"style":441},"$client->loadOrganizations(); \u002F\u002F one request, then cached\n\n$stem = $client->organization('stem');\n$exams = $stem->exams()->list();\n",[361,942,943,960,964,985],{"__ignoreMap":441},[445,944,945,948,950,953,956],{"class":447,"line":448},[445,946,947],{"class":557},"$client",[445,949,696],{"class":533},[445,951,952],{"class":699},"loadOrganizations",[445,954,955],{"class":557},"(); ",[445,957,959],{"class":958},"sLBg1","\u002F\u002F one request, then cached\n",[445,961,962],{"class":447,"line":541},[445,963,545],{"emptyLinePlaceholder":544},[445,965,966,969,971,973,975,978,980,983],{"class":447,"line":548},[445,967,968],{"class":557},"$stem ",[445,970,572],{"class":533},[445,972,693],{"class":557},[445,974,696],{"class":533},[445,976,977],{"class":699},"organization",[445,979,596],{"class":557},[445,981,982],{"class":455},"'stem'",[445,984,625],{"class":557},[445,986,987,990,992,995,997,999,1001,1003,1006],{"class":447,"line":561},[445,988,989],{"class":557},"$exams ",[445,991,572],{"class":533},[445,993,994],{"class":557}," $stem",[445,996,696],{"class":533},[445,998,213],{"class":699},[445,1000,703],{"class":557},[445,1002,696],{"class":533},[445,1004,1005],{"class":699},"list",[445,1007,711],{"class":557},[358,1009,1010],{},"Or take them all at once, keyed by slug:",[436,1012,1014],{"className":524,"code":1013,"language":526,"meta":441,"style":441},"$orgs = $client->loadOrganizations();\n\n$orgs->stem->exams()->list();\n$orgs['coding']->applications()->create($examId, $studentId);\n",[361,1015,1016,1031,1035,1057],{"__ignoreMap":441},[445,1017,1018,1021,1023,1025,1027,1029],{"class":447,"line":448},[445,1019,1020],{"class":557},"$orgs ",[445,1022,572],{"class":533},[445,1024,693],{"class":557},[445,1026,696],{"class":533},[445,1028,952],{"class":699},[445,1030,711],{"class":557},[445,1032,1033],{"class":447,"line":541},[445,1034,545],{"emptyLinePlaceholder":544},[445,1036,1037,1040,1042,1045,1047,1049,1051,1053,1055],{"class":447,"line":548},[445,1038,1039],{"class":557},"$orgs",[445,1041,696],{"class":533},[445,1043,1044],{"class":557},"stem",[445,1046,696],{"class":533},[445,1048,213],{"class":699},[445,1050,703],{"class":557},[445,1052,696],{"class":533},[445,1054,1005],{"class":699},[445,1056,711],{"class":557},[445,1058,1059,1062,1065,1068,1070,1072,1074,1076,1079],{"class":447,"line":561},[445,1060,1061],{"class":557},"$orgs[",[445,1063,1064],{"class":455},"'coding'",[445,1066,1067],{"class":557},"]",[445,1069,696],{"class":533},[445,1071,231],{"class":699},[445,1073,703],{"class":557},[445,1075,696],{"class":533},[445,1077,1078],{"class":699},"create",[445,1080,1081],{"class":557},"($examId, $studentId);\n",[358,1083,1084,1085,1087],{},"A slug your account can't reach throws an exception that names the slugs it can. Passing a raw\n",[361,1086,735],{}," always works and skips the lookup.",[630,1089,1091],{"type":1090},"note",[358,1092,1093,1094,1097,1098,1100,1101,1104,1105,391,1107,417],{},"Operations without an organization in the path, such as students on the core record, countries,\ngrades and organizations, act on ",[384,1095,1096],{},"mto",". Your roles need target ",[361,1099,1096],{}," or ",[361,1102,1103],{},"*"," for those. See\n",[413,1106,22],{"href":23},[413,1108,25],{"href":26},[373,1110,1112],{"id":1111},"a-complete-flow","A complete flow",[358,1114,1115,1116,1118],{},"Register a student, send them into an organization once, then enter them for an exam. The\n",[413,1117,100],{"href":101}," tutorial walks through the same flow over\nplain HTTP.",[1120,1121,1123],"h3",{"id":1122},"step-1-register-the-student","Step 1: register the student",[436,1125,1127],{"className":524,"code":1126,"language":526,"meta":441,"style":441},"use MainTeam\\ApiClient\\Exception\\MtoConflictError;\n\n\u002F\u002F `country` takes an id only. Look it up once and cache it.\n$albania = $client->countries()->findByIso2('AL');\n\n$student = $client->students()->register(\n    firstName: 'Jane',\n    country: $albania->id,\n    grade: '8',                               \u002F\u002F a grade _id, or its name '1' to '12'\n    city: 'Berlin',                           \u002F\u002F an id, or a name within the country\n    school: 'Berlin International School',    \u002F\u002F an id, or a name within country and city\n    lastName: 'Doe',\n    email: 'jane.doe@example.com',\n    birth: '14\u002F05\u002F2010',                      \u002F\u002F DD\u002FMM\u002FYYYY\n    sex: 'f',                                 \u002F\u002F 'm', 'f' or 'n'\n);\n\necho $student->id, ' ', $student->username, PHP_EOL; \u002F\u002F keep the id: every route uses it\n",[361,1128,1129,1138,1142,1147,1175,1179,1201,1213,1226,1242,1258,1274,1287,1300,1317,1334,1339,1344],{"__ignoreMap":441},[445,1130,1131,1133,1136],{"class":447,"line":448},[445,1132,551],{"class":533},[445,1134,1135],{"class":537}," MainTeam\\ApiClient\\Exception\\MtoConflictError",[445,1137,558],{"class":557},[445,1139,1140],{"class":447,"line":541},[445,1141,545],{"emptyLinePlaceholder":544},[445,1143,1144],{"class":447,"line":548},[445,1145,1146],{"class":958},"\u002F\u002F `country` takes an id only. Look it up once and cache it.\n",[445,1148,1149,1152,1154,1156,1158,1161,1163,1165,1168,1170,1173],{"class":447,"line":561},[445,1150,1151],{"class":557},"$albania ",[445,1153,572],{"class":533},[445,1155,693],{"class":557},[445,1157,696],{"class":533},[445,1159,1160],{"class":699},"countries",[445,1162,703],{"class":557},[445,1164,696],{"class":533},[445,1166,1167],{"class":699},"findByIso2",[445,1169,596],{"class":557},[445,1171,1172],{"class":455},"'AL'",[445,1174,625],{"class":557},[445,1176,1177],{"class":447,"line":566},[445,1178,545],{"emptyLinePlaceholder":544},[445,1180,1181,1184,1186,1188,1190,1192,1194,1196,1199],{"class":447,"line":584},[445,1182,1183],{"class":557},"$student ",[445,1185,572],{"class":533},[445,1187,693],{"class":557},[445,1189,696],{"class":533},[445,1191,167],{"class":699},[445,1193,703],{"class":557},[445,1195,696],{"class":533},[445,1197,1198],{"class":699},"register",[445,1200,581],{"class":557},[445,1202,1203,1206,1208,1211],{"class":447,"line":605},[445,1204,1205],{"class":451},"    firstName",[445,1207,590],{"class":557},[445,1209,1210],{"class":455},"'Jane'",[445,1212,779],{"class":557},[445,1214,1215,1218,1221,1223],{"class":447,"line":622},[445,1216,1217],{"class":451},"    country",[445,1219,1220],{"class":557},": $albania",[445,1222,696],{"class":533},[445,1224,1225],{"class":557},"id,\n",[445,1227,1228,1231,1233,1236,1239],{"class":447,"line":889},[445,1229,1230],{"class":451},"    grade",[445,1232,590],{"class":557},[445,1234,1235],{"class":455},"'8'",[445,1237,1238],{"class":557},",                               ",[445,1240,1241],{"class":958},"\u002F\u002F a grade _id, or its name '1' to '12'\n",[445,1243,1244,1247,1249,1252,1255],{"class":447,"line":895},[445,1245,1246],{"class":451},"    city",[445,1248,590],{"class":557},[445,1250,1251],{"class":455},"'Berlin'",[445,1253,1254],{"class":557},",                           ",[445,1256,1257],{"class":958},"\u002F\u002F an id, or a name within the country\n",[445,1259,1260,1263,1265,1268,1271],{"class":447,"line":906},[445,1261,1262],{"class":451},"    school",[445,1264,590],{"class":557},[445,1266,1267],{"class":455},"'Berlin International School'",[445,1269,1270],{"class":557},",    ",[445,1272,1273],{"class":958},"\u002F\u002F an id, or a name within country and city\n",[445,1275,1277,1280,1282,1285],{"class":447,"line":1276},12,[445,1278,1279],{"class":451},"    lastName",[445,1281,590],{"class":557},[445,1283,1284],{"class":455},"'Doe'",[445,1286,779],{"class":557},[445,1288,1290,1293,1295,1298],{"class":447,"line":1289},13,[445,1291,1292],{"class":451},"    email",[445,1294,590],{"class":557},[445,1296,1297],{"class":455},"'jane.doe@example.com'",[445,1299,779],{"class":557},[445,1301,1303,1306,1308,1311,1314],{"class":447,"line":1302},14,[445,1304,1305],{"class":451},"    birth",[445,1307,590],{"class":557},[445,1309,1310],{"class":455},"'14\u002F05\u002F2010'",[445,1312,1313],{"class":557},",                      ",[445,1315,1316],{"class":958},"\u002F\u002F DD\u002FMM\u002FYYYY\n",[445,1318,1320,1323,1325,1328,1331],{"class":447,"line":1319},15,[445,1321,1322],{"class":451},"    sex",[445,1324,590],{"class":557},[445,1326,1327],{"class":455},"'f'",[445,1329,1330],{"class":557},",                                 ",[445,1332,1333],{"class":958},"\u002F\u002F 'm', 'f' or 'n'\n",[445,1335,1337],{"class":447,"line":1336},16,[445,1338,625],{"class":557},[445,1340,1342],{"class":447,"line":1341},17,[445,1343,545],{"emptyLinePlaceholder":544},[445,1345,1347,1349,1352,1354,1357,1360,1363,1365,1368,1370,1373],{"class":447,"line":1346},18,[445,1348,716],{"class":537},[445,1350,1351],{"class":557}," $student",[445,1353,696],{"class":533},[445,1355,1356],{"class":557},"id, ",[445,1358,1359],{"class":455},"' '",[445,1361,1362],{"class":557},", $student",[445,1364,696],{"class":533},[445,1366,1367],{"class":557},"username, ",[445,1369,727],{"class":537},[445,1371,1372],{"class":557},"; ",[445,1374,1375],{"class":958},"\u002F\u002F keep the id: every route uses it\n",[358,1377,1378,1379,1382,1383,417],{},"Required fields are named arguments, so leaving out the country is something your editor catches\ninstead of a ",[361,1380,1381],{},"400",". Anything the API accepts that the client doesn't model goes through ",[361,1384,1385],{},"extra:",[358,1387,1388,736,1391,391,1394,1397,1398,1401],{},[361,1389,1390],{},"grade",[361,1392,1393],{},"city",[361,1395,1396],{},"school"," take an id or a name. The server looks a name up and refuses it with\n",[361,1399,1400],{},"400 bad_request"," if nothing matches. The API never creates a city, a school or a grade. Some\ncountries can't be selected: they're left out of the country list and refused at registration.",[358,1403,1404,1405,1408,1409,1412],{},"Registration is ",[384,1406,1407],{},"not"," idempotent. If a retry after a timeout hits a registration that already\nsucceeded, you get ",[361,1410,1411],{},"409 conflict",":",[436,1414,1416],{"className":524,"code":1415,"language":526,"meta":441,"style":441},"try {\n    $student = $client->students()->register(\u002F* ... *\u002F);\n} catch (MtoConflictError $e) {\n    \u002F\u002F \"A student with that email is already registered to this account (66f2b7c1e4a9d20012ab34cd).\"\n    if (!preg_match('\u002F\\(([0-9a-f]{24})\\)\u002F', $e->getMessage(), $m)) {\n        throw $e; \u002F\u002F another account holds this address: it can't be used\n    }\n    $existingId = $m[1]; \u002F\u002F fetch or update this student instead\n}\n",[361,1417,1418,1426,1452,1469,1474,1516,1527,1532,1551],{"__ignoreMap":441},[445,1419,1420,1423],{"class":447,"line":448},[445,1421,1422],{"class":533},"try",[445,1424,1425],{"class":557}," {\n",[445,1427,1428,1431,1433,1435,1437,1439,1441,1443,1445,1447,1450],{"class":447,"line":541},[445,1429,1430],{"class":557},"    $student ",[445,1432,572],{"class":533},[445,1434,693],{"class":557},[445,1436,696],{"class":533},[445,1438,167],{"class":699},[445,1440,703],{"class":557},[445,1442,696],{"class":533},[445,1444,1198],{"class":699},[445,1446,596],{"class":557},[445,1448,1449],{"class":958},"\u002F* ... *\u002F",[445,1451,625],{"class":557},[445,1453,1454,1457,1460,1463,1466],{"class":447,"line":548},[445,1455,1456],{"class":557},"} ",[445,1458,1459],{"class":533},"catch",[445,1461,1462],{"class":557}," (",[445,1464,1465],{"class":537},"MtoConflictError",[445,1467,1468],{"class":557}," $e) {\n",[445,1470,1471],{"class":447,"line":561},[445,1472,1473],{"class":958},"    \u002F\u002F \"A student with that email is already registered to this account (66f2b7c1e4a9d20012ab34cd).\"\n",[445,1475,1476,1479,1481,1484,1487,1489,1492,1496,1499,1502,1505,1508,1510,1513],{"class":447,"line":566},[445,1477,1478],{"class":533},"    if",[445,1480,1462],{"class":557},[445,1482,1483],{"class":533},"!",[445,1485,1486],{"class":537},"preg_match",[445,1488,596],{"class":557},[445,1490,1491],{"class":455},"'\u002F",[445,1493,1495],{"class":1494},"sJaa3","\\(",[445,1497,1498],{"class":455},"([0-9a-f]{24})",[445,1500,1501],{"class":1494},"\\)",[445,1503,1504],{"class":455},"\u002F'",[445,1506,1507],{"class":557},", $e",[445,1509,696],{"class":533},[445,1511,1512],{"class":699},"getMessage",[445,1514,1515],{"class":557},"(), $m)) {\n",[445,1517,1518,1521,1524],{"class":447,"line":584},[445,1519,1520],{"class":533},"        throw",[445,1522,1523],{"class":557}," $e; ",[445,1525,1526],{"class":958},"\u002F\u002F another account holds this address: it can't be used\n",[445,1528,1529],{"class":447,"line":605},[445,1530,1531],{"class":557},"    }\n",[445,1533,1534,1537,1539,1542,1545,1548],{"class":447,"line":622},[445,1535,1536],{"class":557},"    $existingId ",[445,1538,572],{"class":533},[445,1540,1541],{"class":557}," $m[",[445,1543,1544],{"class":537},"1",[445,1546,1547],{"class":557},"]; ",[445,1549,1550],{"class":958},"\u002F\u002F fetch or update this student instead\n",[445,1552,1553],{"class":447,"line":889},[445,1554,909],{"class":557},[358,1556,1557,1558,417],{},"Store the id when you register so you rarely need this. See ",[413,1559,62],{"href":63},[1120,1561,1563],{"id":1562},"step-2-send-the-student-into-the-organization","Step 2: send the student into the organization",[358,1565,1566],{},"An organization can only refer to a student after the student has signed in there once. That\nfirst sign-in creates the organization's copy. Mint a link when the student clicks, and redirect:",[436,1568,1570],{"className":524,"code":1569,"language":526,"meta":441,"style":441},"$stem = $client->organization('stem');\n$link = $stem->auth()->signinAsStudent($student->id);\n\nheader('Location: ' . $link->url, true, 302);\nexit;\n",[361,1571,1572,1590,1619,1623,1654],{"__ignoreMap":441},[445,1573,1574,1576,1578,1580,1582,1584,1586,1588],{"class":447,"line":448},[445,1575,968],{"class":557},[445,1577,572],{"class":533},[445,1579,693],{"class":557},[445,1581,696],{"class":533},[445,1583,977],{"class":699},[445,1585,596],{"class":557},[445,1587,982],{"class":455},[445,1589,625],{"class":557},[445,1591,1592,1595,1597,1599,1601,1604,1606,1608,1611,1614,1616],{"class":447,"line":541},[445,1593,1594],{"class":557},"$link ",[445,1596,572],{"class":533},[445,1598,994],{"class":557},[445,1600,696],{"class":533},[445,1602,1603],{"class":699},"auth",[445,1605,703],{"class":557},[445,1607,696],{"class":533},[445,1609,1610],{"class":699},"signinAsStudent",[445,1612,1613],{"class":557},"($student",[445,1615,696],{"class":533},[445,1617,1618],{"class":557},"id);\n",[445,1620,1621],{"class":447,"line":548},[445,1622,545],{"emptyLinePlaceholder":544},[445,1624,1625,1628,1630,1633,1636,1639,1641,1644,1647,1649,1652],{"class":447,"line":561},[445,1626,1627],{"class":537},"header",[445,1629,596],{"class":557},[445,1631,1632],{"class":455},"'Location: '",[445,1634,1635],{"class":533}," .",[445,1637,1638],{"class":557}," $link",[445,1640,696],{"class":533},[445,1642,1643],{"class":557},"url, ",[445,1645,1646],{"class":537},"true",[445,1648,736],{"class":557},[445,1650,1651],{"class":537},"302",[445,1653,625],{"class":557},[445,1655,1656,1659],{"class":447,"line":566},[445,1657,1658],{"class":533},"exit",[445,1660,558],{"class":557},[358,1662,1663],{},"The API answers:",[436,1665,1667],{"className":756,"code":1666,"language":758,"meta":441,"style":441},"{\n  \"success\": true,\n  \"message\": \"Sign-in link generated successfully.\",\n  \"data\": {\n    \"url\": \"https:\u002F\u002Fauth.main-team.org\u002F…\",\n    \"organization\": \"stem\",\n    \"studentId\": \"66f2b7c1e4a9d20012ab34cd\",\n    \"expiresIn\": 120\n  }\n}\n",[361,1668,1669,1673,1684,1696,1704,1716,1728,1740,1750,1755],{"__ignoreMap":441},[445,1670,1671],{"class":447,"line":448},[445,1672,765],{"class":557},[445,1674,1675,1678,1680,1682],{"class":447,"line":541},[445,1676,1677],{"class":770},"  \"success\"",[445,1679,590],{"class":557},[445,1681,1646],{"class":537},[445,1683,779],{"class":557},[445,1685,1686,1689,1691,1694],{"class":447,"line":548},[445,1687,1688],{"class":770},"  \"message\"",[445,1690,590],{"class":557},[445,1692,1693],{"class":455},"\"Sign-in link generated successfully.\"",[445,1695,779],{"class":557},[445,1697,1698,1701],{"class":447,"line":561},[445,1699,1700],{"class":770},"  \"data\"",[445,1702,1703],{"class":557},": {\n",[445,1705,1706,1709,1711,1714],{"class":447,"line":566},[445,1707,1708],{"class":770},"    \"url\"",[445,1710,590],{"class":557},[445,1712,1713],{"class":455},"\"https:\u002F\u002Fauth.main-team.org\u002F…\"",[445,1715,779],{"class":557},[445,1717,1718,1721,1723,1726],{"class":447,"line":584},[445,1719,1720],{"class":770},"    \"organization\"",[445,1722,590],{"class":557},[445,1724,1725],{"class":455},"\"stem\"",[445,1727,779],{"class":557},[445,1729,1730,1733,1735,1738],{"class":447,"line":605},[445,1731,1732],{"class":770},"    \"studentId\"",[445,1734,590],{"class":557},[445,1736,1737],{"class":455},"\"66f2b7c1e4a9d20012ab34cd\"",[445,1739,779],{"class":557},[445,1741,1742,1745,1747],{"class":447,"line":622},[445,1743,1744],{"class":770},"    \"expiresIn\"",[445,1746,590],{"class":557},[445,1748,1749],{"class":537},"120\n",[445,1751,1752],{"class":447,"line":889},[445,1753,1754],{"class":557},"  }\n",[445,1756,1757],{"class":447,"line":895},[445,1758,909],{"class":557},[630,1760,1762],{"type":1761},"danger",[358,1763,1764,1767,1768,1771,1772,1775,1776,417],{},[384,1765,1766],{},"A sign-in link is a credential."," It signs whoever opens it in as the student, works ",[384,1769,1770],{},"once",",\nand expires ",[384,1773,1774],{},"120 seconds"," after it was issued. Redirect straight away; never store, log, email or\npaste it. Link previews in chat and mail apps use it up. See\n",[413,1777,80],{"href":81},[358,1779,1780,1781,1783,1784,1787,1788,417],{},"The link is refused with ",[361,1782,922],{}," if the student has no access to that organization, and\nwith ",[361,1785,1786],{},"404 not_found"," if the student isn't one of yours. See\n",[413,1789,103],{"href":104},[1120,1791,1793],{"id":1792},"step-3-choose-an-exam-for-the-student","Step 3: choose an exam for the student",[358,1795,1796,1797,1800,1801,1806,1807,1810],{},"Use the exams ",[384,1798,1799],{},"this student"," can take. The per-student picker,\n",[413,1802,1803],{"href":226},[361,1804,1805],{},"GET \u002Fv1\u002F{organizationId}\u002Fexam\u002Favailable\u002F{studentId}",",\napplies the student's grade and country and leaves out what they already hold. Its tree runs\ncategory, then session (date), then language, and every leaf carries a ",[361,1808,1809],{},"matchedExam",". The package\nREADME names the method that calls it. The organization's open exams are also available as a plain\nlist:",[436,1812,1814],{"className":524,"code":1813,"language":526,"meta":441,"style":441},"$openExams = $stem->exams()->list(); \u002F\u002F open exams only, for every student\n",[361,1815,1816],{"__ignoreMap":441},[445,1817,1818,1821,1823,1825,1827,1829,1831,1833,1835,1837],{"class":447,"line":448},[445,1819,1820],{"class":557},"$openExams ",[445,1822,572],{"class":533},[445,1824,994],{"class":557},[445,1826,696],{"class":533},[445,1828,213],{"class":699},[445,1830,703],{"class":557},[445,1832,696],{"class":533},[445,1834,1005],{"class":699},[445,1836,955],{"class":557},[445,1838,1839],{"class":958},"\u002F\u002F open exams only, for every student\n",[358,1841,1842,1843,417],{},"A closed exam isn't in that list, can't be read by id and can't be applied to. See\n",[413,1844,83],{"href":84},[1120,1846,1848],{"id":1847},"step-4-enter-the-student","Step 4: enter the student",[436,1850,1852],{"className":524,"code":1851,"language":526,"meta":441,"style":441},"$application = $stem->applications()->create($examId, $student->id);\n\u002F\u002F $examId is the picker leaf's matchedExam _id; the student id is the core id from registration.\n",[361,1853,1854,1880],{"__ignoreMap":441},[445,1855,1856,1859,1861,1863,1865,1867,1869,1871,1873,1876,1878],{"class":447,"line":448},[445,1857,1858],{"class":557},"$application ",[445,1860,572],{"class":533},[445,1862,994],{"class":557},[445,1864,696],{"class":533},[445,1866,231],{"class":699},[445,1868,703],{"class":557},[445,1870,696],{"class":533},[445,1872,1078],{"class":699},[445,1874,1875],{"class":557},"($examId, $student",[445,1877,696],{"class":533},[445,1879,1618],{"class":557},[445,1881,1882],{"class":447,"line":541},[445,1883,1884],{"class":958},"\u002F\u002F $examId is the picker leaf's matchedExam _id; the student id is the core id from registration.\n",[468,1886,1887,1897],{},[471,1888,1889],{},[474,1890,1891,1894],{},[477,1892,1893],{},"Answer",[477,1895,1896],{},"Meaning",[487,1898,1899,1913,1926,1935],{},[474,1900,1901,1910],{},[492,1902,1903,1906,1907],{},[361,1904,1905],{},"201"," ",[361,1908,1909],{},"\"Application created successfully.\"",[492,1911,1912],{},"A new application.",[474,1914,1915,1923],{},[492,1916,1917,1906,1920],{},[361,1918,1919],{},"200",[361,1921,1922],{},"\"Application already exists.\"",[492,1924,1925],{},"The student already holds this exam. You get the existing application, so a retry is safe.",[474,1927,1928,1932],{},[492,1929,1930],{},[361,1931,1411],{},[492,1933,1934],{},"The picker wouldn't offer this exam (closed, grade, country, no language, or a clash in the same category on the same sitting), or the student has never signed in to this organization. The message says which.",[474,1936,1937,1941],{},[492,1938,1939],{},[361,1940,1786],{},[492,1942,1943],{},"The exam or the student doesn't exist, or the student isn't yours.",[358,1945,1946,1947,1949],{},"See ",[413,1948,86],{"href":87}," for moving and deleting applications, and how\npayments behave.",[373,1951,1953],{"id":1952},"exceptions","Exceptions",[358,1955,1956,1957,1960,1961,1964,1965,1412],{},"There is one exception class per code in the ",[413,1958,1959],{"href":43},"error reference",", under the\n",[361,1962,1963],{},"MainTeam\\ApiClient\\Exception"," namespace, and all of them extend ",[361,1966,1967],{},"MtoError",[436,1969,1971],{"className":524,"code":1970,"language":526,"meta":441,"style":441},"use MainTeam\\ApiClient\\Exception\\MtoConflictError;\nuse MainTeam\\ApiClient\\Exception\\MtoError;\n\ntry {\n    $stem->applications()->create($examId, $studentId);\n} catch (MtoConflictError $e) {\n    error_log($e->getMessage() . ' ' . $e->documentationUrl);\n} catch (MtoError $e) {\n    error_log(\"{$e->errorCode} (HTTP {$e->status})\");\n}\n",[361,1972,1973,1981,1990,1994,2000,2017,2029,2059,2071,2103],{"__ignoreMap":441},[445,1974,1975,1977,1979],{"class":447,"line":448},[445,1976,551],{"class":533},[445,1978,1135],{"class":537},[445,1980,558],{"class":557},[445,1982,1983,1985,1988],{"class":447,"line":541},[445,1984,551],{"class":533},[445,1986,1987],{"class":537}," MainTeam\\ApiClient\\Exception\\MtoError",[445,1989,558],{"class":557},[445,1991,1992],{"class":447,"line":548},[445,1993,545],{"emptyLinePlaceholder":544},[445,1995,1996,1998],{"class":447,"line":561},[445,1997,1422],{"class":533},[445,1999,1425],{"class":557},[445,2001,2002,2005,2007,2009,2011,2013,2015],{"class":447,"line":566},[445,2003,2004],{"class":557},"    $stem",[445,2006,696],{"class":533},[445,2008,231],{"class":699},[445,2010,703],{"class":557},[445,2012,696],{"class":533},[445,2014,1078],{"class":699},[445,2016,1081],{"class":557},[445,2018,2019,2021,2023,2025,2027],{"class":447,"line":584},[445,2020,1456],{"class":557},[445,2022,1459],{"class":533},[445,2024,1462],{"class":557},[445,2026,1465],{"class":537},[445,2028,1468],{"class":557},[445,2030,2031,2034,2037,2039,2041,2044,2046,2049,2051,2054,2056],{"class":447,"line":605},[445,2032,2033],{"class":537},"    error_log",[445,2035,2036],{"class":557},"($e",[445,2038,696],{"class":533},[445,2040,1512],{"class":699},[445,2042,2043],{"class":557},"() ",[445,2045,417],{"class":533},[445,2047,2048],{"class":455}," ' '",[445,2050,1635],{"class":533},[445,2052,2053],{"class":557}," $e",[445,2055,696],{"class":533},[445,2057,2058],{"class":557},"documentationUrl);\n",[445,2060,2061,2063,2065,2067,2069],{"class":447,"line":622},[445,2062,1456],{"class":557},[445,2064,1459],{"class":533},[445,2066,1462],{"class":557},[445,2068,1967],{"class":537},[445,2070,1468],{"class":557},[445,2072,2073,2075,2077,2080,2083,2085,2088,2091,2093,2095,2098,2101],{"class":447,"line":889},[445,2074,2033],{"class":537},[445,2076,596],{"class":557},[445,2078,2079],{"class":455},"\"{",[445,2081,2082],{"class":557},"$e",[445,2084,696],{"class":533},[445,2086,2087],{"class":557},"errorCode",[445,2089,2090],{"class":455},"} (HTTP {",[445,2092,2082],{"class":557},[445,2094,696],{"class":533},[445,2096,2097],{"class":557},"status",[445,2099,2100],{"class":455},"})\"",[445,2102,625],{"class":557},[445,2104,2105],{"class":447,"line":895},[445,2106,909],{"class":557},[358,2108,2109,2110,2113,2114,2117,2118,2121,2122,2125],{},"The API's code, such as ",[361,2111,2112],{},"conflict",", is on ",[361,2115,2116],{},"$e->errorCode",", not ",[361,2119,2120],{},"getCode()",". PHP won't let a\nsubclass redeclare the inherited ",[361,2123,2124],{},"Throwable::$code"," as a string.",[358,2127,2128,2129,2132,2133,2136,2137,2140,2141,2144],{},"Calls that can't succeed are rejected before the request as ",[361,2130,2131],{},"MtoValidationError",". It extends\n",[361,2134,2135],{},"MtoBadRequestError"," and carries the same ",[361,2138,2139],{},"bad_request"," \u002F 400 the server would have returned, so\none ",[361,2142,2143],{},"catch (MtoBadRequestError $e)"," covers both.",[358,2146,2147,2148,391,2150,2152],{},"Branch on the class or on ",[361,2149,2087],{},[361,2151,2097],{},". The message is text for people and can\nchange.",[468,2154,2155,2168],{},[471,2156,2157],{},[474,2158,2159,2162,2165],{},[477,2160,2161],{},"Status and code",[477,2163,2164],{},"Retry?",[477,2166,2167],{},"Typical fix",[487,2169,2170,2182,2193,2206,2217,2228,2244],{},[474,2171,2172,2176,2179],{},[492,2173,2174],{},[361,2175,1400],{},[492,2177,2178],{},"No",[492,2180,2181],{},"Fix the request. The message names the field.",[474,2183,2184,2188,2190],{},[492,2185,2186],{},[361,2187,915],{},[492,2189,2178],{},[492,2191,2192],{},"Check the key, the secret and your clock.",[474,2194,2195,2199,2201],{},[492,2196,2197],{},[361,2198,922],{},[492,2200,2178],{},[492,2202,2203,2204,417],{},"Ask your operator for the permission. See ",[413,2205,25],{"href":26},[474,2207,2208,2212,2214],{},[492,2209,2210],{},[361,2211,1786],{},[492,2213,2178],{},[492,2215,2216],{},"Check the id, and that the record is yours.",[474,2218,2219,2223,2225],{},[492,2220,2221],{},[361,2222,1411],{},[492,2224,2178],{},[492,2226,2227],{},"Read the message: it names the rule.",[474,2229,2230,2235,2241],{},[492,2231,2232],{},[361,2233,2234],{},"429 too_many_requests",[492,2236,2237,2238],{},"Yes, after ",[361,2239,2240],{},"Retry-After",[492,2242,2243],{},"Slow down. See below.",[474,2245,2246,2251,2254],{},[492,2247,2248],{},[361,2249,2250],{},"500 internal_error",[492,2252,2253],{},"Yes, with backoff",[492,2255,2256,2257,417],{},"Retry later. If it persists, report the ",[361,2258,2259],{},"request_id",[373,2261,39],{"id":2262},"pagination",[358,2264,2265,2266,1412],{},"A list call returns a page you can iterate, with its metadata on ",[361,2267,2268],{},"->pagination",[436,2270,2272],{"className":524,"code":2271,"language":526,"meta":441,"style":441},"$page = $client->students()->list(page: 2, limit: 50);\n\nforeach ($page as $student) {\n    echo $student->username, PHP_EOL;\n}\necho $page->pagination->total, PHP_EOL;\n",[361,2273,2274,2315,2319,2333,2348,2352],{"__ignoreMap":441},[445,2275,2276,2279,2281,2283,2285,2287,2289,2291,2293,2295,2298,2300,2303,2305,2308,2310,2313],{"class":447,"line":448},[445,2277,2278],{"class":557},"$page ",[445,2280,572],{"class":533},[445,2282,693],{"class":557},[445,2284,696],{"class":533},[445,2286,167],{"class":699},[445,2288,703],{"class":557},[445,2290,696],{"class":533},[445,2292,1005],{"class":699},[445,2294,596],{"class":557},[445,2296,2297],{"class":451},"page",[445,2299,590],{"class":557},[445,2301,2302],{"class":537},"2",[445,2304,736],{"class":557},[445,2306,2307],{"class":451},"limit",[445,2309,590],{"class":557},[445,2311,2312],{"class":537},"50",[445,2314,625],{"class":557},[445,2316,2317],{"class":447,"line":541},[445,2318,545],{"emptyLinePlaceholder":544},[445,2320,2321,2324,2327,2330],{"class":447,"line":548},[445,2322,2323],{"class":533},"foreach",[445,2325,2326],{"class":557}," ($page ",[445,2328,2329],{"class":533},"as",[445,2331,2332],{"class":557}," $student) {\n",[445,2334,2335,2338,2340,2342,2344,2346],{"class":447,"line":561},[445,2336,2337],{"class":537},"    echo",[445,2339,1351],{"class":557},[445,2341,696],{"class":533},[445,2343,1367],{"class":557},[445,2345,727],{"class":537},[445,2347,558],{"class":557},[445,2349,2350],{"class":447,"line":566},[445,2351,909],{"class":557},[445,2353,2354,2356,2359,2361,2363,2365,2368,2370],{"class":447,"line":584},[445,2355,716],{"class":537},[445,2357,2358],{"class":557}," $page",[445,2360,696],{"class":533},[445,2362,2262],{"class":557},[445,2364,696],{"class":533},[445,2366,2367],{"class":557},"total, ",[445,2369,727],{"class":537},[445,2371,558],{"class":557},[358,2373,2374,2376,2377,2379,2380,2382,2383,2386],{},[361,2375,2297],{}," starts at 1; ",[361,2378,2307],{}," defaults to 20 and is capped at 100. A larger ",[361,2381,2307],{}," is lowered to 100\nrather than refused. ",[361,2384,2385],{},"Pagination::walk()"," walks every page:",[436,2388,2390],{"className":524,"code":2389,"language":526,"meta":441,"style":441},"use MainTeam\\ApiClient\\Pagination\\Pagination;\n\nforeach (Pagination::walk(fn ($page, $limit) => $client->students()->list($page, $limit)) as $student) {\n    echo $student->username, PHP_EOL;\n}\n",[361,2391,2392,2401,2405,2444,2458],{"__ignoreMap":441},[445,2393,2394,2396,2399],{"class":447,"line":448},[445,2395,551],{"class":533},[445,2397,2398],{"class":537}," MainTeam\\ApiClient\\Pagination\\Pagination",[445,2400,558],{"class":557},[445,2402,2403],{"class":447,"line":541},[445,2404,545],{"emptyLinePlaceholder":544},[445,2406,2407,2409,2411,2413,2416,2419,2421,2424,2427,2429,2431,2433,2435,2437,2440,2442],{"class":447,"line":548},[445,2408,2323],{"class":533},[445,2410,1462],{"class":557},[445,2412,39],{"class":537},[445,2414,2415],{"class":533},"::",[445,2417,2418],{"class":699},"walk",[445,2420,596],{"class":557},[445,2422,2423],{"class":533},"fn",[445,2425,2426],{"class":557}," ($page, $limit) => $client",[445,2428,696],{"class":533},[445,2430,167],{"class":699},[445,2432,703],{"class":557},[445,2434,696],{"class":533},[445,2436,1005],{"class":699},[445,2438,2439],{"class":557},"($page, $limit)) ",[445,2441,2329],{"class":533},[445,2443,2332],{"class":557},[445,2445,2446,2448,2450,2452,2454,2456],{"class":447,"line":561},[445,2447,2337],{"class":537},[445,2449,1351],{"class":557},[445,2451,696],{"class":533},[445,2453,1367],{"class":557},[445,2455,727],{"class":537},[445,2457,558],{"class":557},[445,2459,2460],{"class":447,"line":566},[445,2461,909],{"class":557},[358,2463,2464,2465,417],{},"Each page is a request against that route's rate limit, so at 100 per page, 5,000 students cost 50\nrequests. See ",[413,2466,39],{"href":40},[373,2468,2470],{"id":2469},"downloads","Downloads",[358,2472,2473,2474,2476,2477,1412],{},"Certificates and reports download as PDFs. Only released documents can be listed or downloaded.\nPass the document's ",[361,2475,735],{}," or its ",[361,2478,2479],{},"shortId",[436,2481,2483],{"className":524,"code":2482,"language":526,"meta":441,"style":441},"$file = $stem->certificates()->download($certificateIdOrShortId);\n\n$file->saveTo('\u002Fvar\u002Fexports');   \u002F\u002F saves under the server's own file name\n$bytes = $file->contents();      \u002F\u002F or take the bytes\n$stream = $file->stream();       \u002F\u002F or pipe it somewhere\n",[361,2484,2485,2509,2513,2534,2555],{"__ignoreMap":441},[445,2486,2487,2490,2492,2494,2496,2499,2501,2503,2506],{"class":447,"line":448},[445,2488,2489],{"class":557},"$file ",[445,2491,572],{"class":533},[445,2493,994],{"class":557},[445,2495,696],{"class":533},[445,2497,2498],{"class":699},"certificates",[445,2500,703],{"class":557},[445,2502,696],{"class":533},[445,2504,2505],{"class":699},"download",[445,2507,2508],{"class":557},"($certificateIdOrShortId);\n",[445,2510,2511],{"class":447,"line":541},[445,2512,545],{"emptyLinePlaceholder":544},[445,2514,2515,2518,2520,2523,2525,2528,2531],{"class":447,"line":548},[445,2516,2517],{"class":557},"$file",[445,2519,696],{"class":533},[445,2521,2522],{"class":699},"saveTo",[445,2524,596],{"class":557},[445,2526,2527],{"class":455},"'\u002Fvar\u002Fexports'",[445,2529,2530],{"class":557},");   ",[445,2532,2533],{"class":958},"\u002F\u002F saves under the server's own file name\n",[445,2535,2536,2539,2541,2544,2546,2549,2552],{"class":447,"line":561},[445,2537,2538],{"class":557},"$bytes ",[445,2540,572],{"class":533},[445,2542,2543],{"class":557}," $file",[445,2545,696],{"class":533},[445,2547,2548],{"class":699},"contents",[445,2550,2551],{"class":557},"();      ",[445,2553,2554],{"class":958},"\u002F\u002F or take the bytes\n",[445,2556,2557,2560,2562,2564,2566,2569,2572],{"class":447,"line":566},[445,2558,2559],{"class":557},"$stream ",[445,2561,572],{"class":533},[445,2563,2543],{"class":557},[445,2565,696],{"class":533},[445,2567,2568],{"class":699},"stream",[445,2570,2571],{"class":557},"();       ",[445,2573,2574],{"class":958},"\u002F\u002F or pipe it somewhere\n",[358,2576,2577,2578,2581,2582,2584,2585,417],{},"The body is held in a ",[361,2579,2580],{},"php:\u002F\u002Ftemp"," stream, so a large PDF spills to disk instead of sitting in\nmemory. A document that doesn't exist, isn't released, isn't yours or has no file all answer the\nsame ",[361,2583,1786],{},". See ",[413,2586,92],{"href":93},[373,2588,2590],{"id":2589},"rate-limits-and-retries","Rate limits and retries",[358,2592,2593,2594,2597,2598,2601,2602,736,2605,2608,2609,2612,2613,2615,2616,2618],{},"The limit is ",[384,2595,2596],{},"100 requests per 60 seconds, per account, per operation"," (each operation counted\non its own; see ",[413,2599,45],{"href":2600},"\u002Fapi\u002Frate-limits#what-per-operation-means","), counted for your account\nwherever you call from. Counted responses carry ",[361,2603,2604],{},"X-RateLimit-Limit",[361,2606,2607],{},"X-RateLimit-Remaining"," and\n",[361,2610,2611],{},"X-RateLimit-Reset",". A ",[361,2614,2234],{}," carries ",[361,2617,2240],{}," in seconds.",[358,2620,2621,2622,2624],{},"Wait at least that long. Requests sent during a block are refused too, but they don't extend it,\nand a block never outlasts one 60-second window. If your code doesn't have ",[361,2623,2240],{}," to hand,\n60 seconds is always enough:",[436,2626,2628],{"className":524,"code":2627,"language":526,"meta":441,"style":441},"use MainTeam\\ApiClient\\Exception\\MtoError;\n\nfunction withRateLimitRetry(callable $call, int $attempts = 3): mixed\n{\n    for ($attempt = 1; ; $attempt++) {\n        try {\n            return $call();\n        } catch (MtoError $e) {\n            if ($e->status !== 429 || $attempt === $attempts) {\n                throw $e;\n            }\n            sleep(60); \u002F\u002F one full window always clears a block\n        }\n    }\n}\n\n$page = withRateLimitRetry(fn () => $client->students()->list(page: 1, limit: 100));\n",[361,2629,2630,2638,2642,2677,2681,2703,2710,2718,2731,2762,2770,2775,2791,2796,2800,2804,2808],{"__ignoreMap":441},[445,2631,2632,2634,2636],{"class":447,"line":448},[445,2633,551],{"class":533},[445,2635,1987],{"class":537},[445,2637,558],{"class":557},[445,2639,2640],{"class":447,"line":541},[445,2641,545],{"emptyLinePlaceholder":544},[445,2643,2644,2647,2650,2652,2655,2658,2661,2664,2666,2669,2672,2674],{"class":447,"line":548},[445,2645,2646],{"class":533},"function",[445,2648,2649],{"class":699}," withRateLimitRetry",[445,2651,596],{"class":557},[445,2653,2654],{"class":533},"callable",[445,2656,2657],{"class":557}," $call, ",[445,2659,2660],{"class":533},"int",[445,2662,2663],{"class":557}," $attempts ",[445,2665,572],{"class":533},[445,2667,2668],{"class":537}," 3",[445,2670,2671],{"class":557},")",[445,2673,1412],{"class":533},[445,2675,2676],{"class":533}," mixed\n",[445,2678,2679],{"class":447,"line":561},[445,2680,765],{"class":557},[445,2682,2683,2686,2689,2691,2694,2697,2700],{"class":447,"line":566},[445,2684,2685],{"class":533},"    for",[445,2687,2688],{"class":557}," ($attempt ",[445,2690,572],{"class":533},[445,2692,2693],{"class":537}," 1",[445,2695,2696],{"class":557},"; ; $attempt",[445,2698,2699],{"class":533},"++",[445,2701,2702],{"class":557},") {\n",[445,2704,2705,2708],{"class":447,"line":584},[445,2706,2707],{"class":533},"        try",[445,2709,1425],{"class":557},[445,2711,2712,2715],{"class":447,"line":605},[445,2713,2714],{"class":533},"            return",[445,2716,2717],{"class":557}," $call();\n",[445,2719,2720,2723,2725,2727,2729],{"class":447,"line":622},[445,2721,2722],{"class":557},"        } ",[445,2724,1459],{"class":533},[445,2726,1462],{"class":557},[445,2728,1967],{"class":537},[445,2730,1468],{"class":557},[445,2732,2733,2736,2739,2741,2744,2747,2750,2753,2756,2759],{"class":447,"line":889},[445,2734,2735],{"class":533},"            if",[445,2737,2738],{"class":557}," ($e",[445,2740,696],{"class":533},[445,2742,2743],{"class":557},"status ",[445,2745,2746],{"class":533},"!==",[445,2748,2749],{"class":537}," 429",[445,2751,2752],{"class":533}," ||",[445,2754,2755],{"class":557}," $attempt ",[445,2757,2758],{"class":533},"===",[445,2760,2761],{"class":557}," $attempts) {\n",[445,2763,2764,2767],{"class":447,"line":895},[445,2765,2766],{"class":533},"                throw",[445,2768,2769],{"class":557}," $e;\n",[445,2771,2772],{"class":447,"line":906},[445,2773,2774],{"class":557},"            }\n",[445,2776,2777,2780,2782,2785,2788],{"class":447,"line":1276},[445,2778,2779],{"class":537},"            sleep",[445,2781,596],{"class":557},[445,2783,2784],{"class":537},"60",[445,2786,2787],{"class":557},"); ",[445,2789,2790],{"class":958},"\u002F\u002F one full window always clears a block\n",[445,2792,2793],{"class":447,"line":1289},[445,2794,2795],{"class":557},"        }\n",[445,2797,2798],{"class":447,"line":1302},[445,2799,1531],{"class":557},[445,2801,2802],{"class":447,"line":1319},[445,2803,909],{"class":557},[445,2805,2806],{"class":447,"line":1336},[445,2807,545],{"emptyLinePlaceholder":544},[445,2809,2810,2812,2814,2816,2818,2820,2823,2825,2827,2829,2831,2833,2835,2837,2839,2841,2843,2845,2847,2850],{"class":447,"line":1341},[445,2811,2278],{"class":557},[445,2813,572],{"class":533},[445,2815,2649],{"class":699},[445,2817,596],{"class":557},[445,2819,2423],{"class":533},[445,2821,2822],{"class":557}," () => $client",[445,2824,696],{"class":533},[445,2826,167],{"class":699},[445,2828,703],{"class":557},[445,2830,696],{"class":533},[445,2832,1005],{"class":699},[445,2834,596],{"class":557},[445,2836,2297],{"class":451},[445,2838,590],{"class":557},[445,2840,1544],{"class":537},[445,2842,736],{"class":557},[445,2844,2307],{"class":451},[445,2846,590],{"class":557},[445,2848,2849],{"class":537},"100",[445,2851,2852],{"class":557},"));\n",[358,2854,2855,2856,2859,2860,2863,2864,2867,2868,417],{},"Only retry what is safe to repeat: reads, and ",[361,2857,2858],{},"POST \u002Fapplication"," for the same exam. Don't retry\n",[361,2861,2862],{},"POST \u002Fstudent"," blindly (see step 1), don't retry other ",[361,2865,2866],{},"4xx"," answers, and never retry a sign-in\nlink. See ",[413,2869,2870],{"href":49},"Retries and idempotency",[373,2872,2874],{"id":2873},"models","Models",[358,2876,2877],{},"Models have typed properties for the documented fields and keep everything else:",[436,2879,2881],{"className":524,"code":2880,"language":526,"meta":441,"style":441},"echo $student->username;          \u002F\u002F a documented field\necho $student['somethingNew'];    \u002F\u002F a field added after your package version\nvar_dump($student->raw);          \u002F\u002F the whole decoded object\n",[361,2882,2883,2897,2913],{"__ignoreMap":441},[445,2884,2885,2887,2889,2891,2894],{"class":447,"line":448},[445,2886,716],{"class":537},[445,2888,1351],{"class":557},[445,2890,696],{"class":533},[445,2892,2893],{"class":557},"username;          ",[445,2895,2896],{"class":958},"\u002F\u002F a documented field\n",[445,2898,2899,2901,2904,2907,2910],{"class":447,"line":541},[445,2900,716],{"class":537},[445,2902,2903],{"class":557}," $student[",[445,2905,2906],{"class":455},"'somethingNew'",[445,2908,2909],{"class":557},"];    ",[445,2911,2912],{"class":958},"\u002F\u002F a field added after your package version\n",[445,2914,2915,2918,2920,2922,2925],{"class":447,"line":548},[445,2916,2917],{"class":537},"var_dump",[445,2919,1613],{"class":557},[445,2921,696],{"class":533},[445,2923,2924],{"class":557},"raw);          ",[445,2926,2927],{"class":958},"\u002F\u002F the whole decoded object\n",[358,2929,2930,2931,2934,2935,2938],{},"Only ",[361,2932,2933],{},"id"," is guaranteed. Every other property is nullable, and a field that arrives with an\nunexpected type reads as ",[361,2936,2937],{},"null"," instead of throwing. That's what lets an older package keep working\nwhen the API adds fields.",[358,2940,2941],{},"Two response details catch people out:",[378,2943,2944,2992],{},[381,2945,2946,2949,2950,736,2953,736,2955,779,2957,736,2959,391,2962,2965,2966,2969,2970,2973,2974,1462,2977,736,2979,736,2982,736,2985,2988,2989,2991],{},[384,2947,2948],{},"Reads resolve their references."," A student read carries ",[361,2951,2952],{},"country",[361,2954,1393],{},[361,2956,1396],{},[361,2958,1390],{},[361,2960,2961],{},"supervisor",[361,2963,2964],{},"partner"," as objects when they're set. An application read carries its\n",[361,2967,2968],{},"exam",", its ",[361,2971,2972],{},"payment"," and a short ",[361,2975,2976],{},"user",[361,2978,735],{},[361,2980,2981],{},"mainId",[361,2983,2984],{},"firstName",[361,2986,2987],{},"lastName","). The\nexception is the application listing by exam, where ",[361,2990,2968],{}," stays an id. Registration and updates\nanswer with the reference fields as ids.",[381,2993,2994,3001,3002,3004,3005,3007,3008,417],{},[384,2995,2996,2997,391,2999,417],{},"The student on an application carries ",[361,2998,735],{},[361,3000,2981],{}," The ",[361,3003,2976],{}," on an application is\nthe organization's copy of the student, so match on its ",[361,3006,2981],{},", which is the id you registered\nthem with. See ",[413,3009,36],{"href":37},[373,3011,3013],{"id":3012},"a-different-http-client","A different HTTP client",[358,3015,3016,3019,3020,3023,3024,3027],{},[361,3017,3018],{},"CurlTransport"," is the default. Implement the ",[361,3021,3022],{},"Transport"," interface to run the client through\nPSR-18, a proxy, or a fixture in your tests. The package README shows the adapter. Whatever you\nplug in, keep TLS certificate verification on, and keep the ",[361,3025,3026],{},"Authorization"," header out of your\ntransport's logs.",[373,3029,3031],{"id":3030},"next-steps","Next steps",[378,3033,3034,3039,3044],{},[381,3035,3036,3038],{},[413,3037,100],{"href":101},": the full flow, end to end.",[381,3040,3041,3043],{},[413,3042,109],{"href":110},": a nightly job for certificates and reports.",[381,3045,3046,391,3049,417],{},[413,3047,3048],{"href":309},"Node.js client",[413,3050,314],{"href":315},[3052,3053,3054],"style",{},"html pre.shiki code .soyes, html code.shiki .soyes{--shiki-default:#702C00;--shiki-dark:#FFB757}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 .sHUrx, html code.shiki .sHUrx{--shiki-default:#A0111F;--shiki-dark:#FF9492}html pre.shiki code .s-5SL, html code.shiki .s-5SL{--shiki-default:#023B95;--shiki-dark:#91CBFF}html pre.shiki code .suds8, html code.shiki .suds8{--shiki-default:#0E1116;--shiki-dark:#F0F3F6}html pre.shiki code .sKwhi, html code.shiki .sKwhi{--shiki-default:#622CBC;--shiki-dark:#DBB7FF}html pre.shiki code .sne4z, html code.shiki .sne4z{--shiki-default:#024C1A;--shiki-dark:#72F088}html pre.shiki code .sLBg1, html code.shiki .sLBg1{--shiki-default:#66707B;--shiki-dark:#BDC4CC}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":441,"searchDepth":541,"depth":548,"links":3056},[3057,3058,3059,3060,3061,3062,3068,3069,3070,3071,3072,3073,3074],{"id":375,"depth":541,"text":376},{"id":420,"depth":541,"text":421},{"id":462,"depth":541,"text":463},{"id":665,"depth":541,"text":666},{"id":930,"depth":541,"text":931},{"id":1111,"depth":541,"text":1112,"children":3063},[3064,3065,3066,3067],{"id":1122,"depth":548,"text":1123},{"id":1562,"depth":548,"text":1563},{"id":1792,"depth":548,"text":1793},{"id":1847,"depth":548,"text":1848},{"id":1952,"depth":541,"text":1953},{"id":2262,"depth":541,"text":39},{"id":2469,"depth":541,"text":2470},{"id":2589,"depth":541,"text":2590},{"id":2873,"depth":541,"text":2874},{"id":3012,"depth":541,"text":3013},{"id":3030,"depth":541,"text":3031},"Install and use main-team\u002Fapi-client for PHP 8.1+. Covers setup, organizations, a complete flow, exceptions, pagination, downloads, models and transports.","md",{},[3079,3080,3081,3082,3083,3084,3085,3086,3087,3088],"getCurrentApiAccount","listOrganizations","listCountries","registerStudent","createSigninLink","listExams","listAvailableExams","createApplication","listStudents","downloadCertificate","30",{"title":353,"description":3075},"api\u002Fclients\u002Fphp","u7I5VypegJTmlyqAL_q5wU7_ehRaqf-PhAsvAJ4GEUQ",[3094,3099,3103,3107,3111,3116,3121,3125,3130,3133],{"operationId":3079,"slug":3095,"method":133,"path":3096,"tag":77,"summary":143,"deprecated":134,"public":134,"permission":3097,"scope":3098,"order":548},"get-current-api-account","\u002Fv1\u002Fapi-account\u002Fvalidate-me","api\u002F*:$org:$ID","flat",{"operationId":3080,"slug":3100,"method":133,"path":3101,"tag":74,"summary":161,"deprecated":134,"public":134,"permission":3102,"scope":3098,"order":622},"list-organizations","\u002Fv1\u002Forganization","organization\u002Fread:$org:$ID",{"operationId":3081,"slug":3104,"method":133,"path":3105,"tag":74,"summary":149,"deprecated":134,"public":134,"permission":3106,"scope":3098,"order":561},"list-countries","\u002Fv1\u002Fcountry","country\u002Fread:$org:$ID",{"operationId":3082,"slug":3108,"method":141,"path":3109,"tag":62,"summary":173,"deprecated":134,"public":134,"permission":3110,"scope":3098,"order":906},"register-student","\u002Fv1\u002Fstudent","student\u002Fcreate:$org:$ID",{"operationId":3083,"slug":3112,"method":141,"path":3113,"tag":80,"summary":210,"deprecated":134,"public":134,"permission":3114,"scope":977,"order":3115},"create-signin-link","\u002Fv1\u002F{organizationId}\u002Fauth\u002Fsignin","auth\u002Fsignin:$org:$ID",22,{"operationId":3084,"slug":3117,"method":133,"path":3118,"tag":83,"summary":216,"deprecated":134,"public":134,"permission":3119,"scope":977,"order":3120},"list-exams","\u002Fv1\u002F{organizationId}\u002Fexam","exam\u002Fread:$org:$ID",23,{"operationId":3085,"slug":3122,"method":133,"path":3123,"tag":83,"summary":225,"deprecated":134,"public":134,"permission":3119,"scope":977,"order":3124},"list-available-exams","\u002Fv1\u002F{organizationId}\u002Fexam\u002Favailable\u002F{studentId}",26,{"operationId":3086,"slug":3126,"method":141,"path":3127,"tag":86,"summary":237,"deprecated":134,"public":134,"permission":3128,"scope":977,"order":3129},"create-application","\u002Fv1\u002F{organizationId}\u002Fapplication","application\u002Fcreate:$org:$ID",29,{"operationId":3087,"slug":3131,"method":133,"path":3109,"tag":62,"summary":170,"deprecated":134,"public":134,"permission":3132,"scope":3098,"order":895},"list-students","student\u002Fread:$org:$ID",{"operationId":3088,"slug":3134,"method":133,"path":3135,"tag":256,"summary":260,"deprecated":134,"public":134,"permission":3136,"scope":977,"order":3137},"download-certificate","\u002Fv1\u002F{organizationId}\u002Fcertificate\u002Fdownload\u002F{certificateId}","certificate\u002Fread:$org:$ID",35,1791554615383]